• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

barseghyanartur / faker-file-ui / 6365814295

30 Sep 2023 09:48PM UTC coverage: 78.498% (+10.7%) from 67.803%
6365814295

push

github-actions

barseghyanartur
Fix tests

85 of 115 branches covered (0.0%)

Branch coverage included in aggregate %.

145 of 178 relevant lines covered (81.46%)

3219.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.05
/src/FileGenerator.js
1
import * as React from "react";
2
import { useState, useEffect } from "react";
3
import { createTheme, ThemeProvider } from "@mui/material/styles";
4
import Collapse from '@mui/material/Collapse';
5
import Checkbox from "@mui/material/Checkbox";
6
import FormControl from "@mui/material/FormControl";
7
import FormControlLabel from "@mui/material/FormControlLabel";
8
import {
9
  Grid,
10
  Link,
11
  List,
12
  ListItem,
13
  ListItemButton,
14
  ListItemText,
15
  TextField,
16
  Typography,
17
  Button,
18
} from "@mui/material";
19

20
import Accordion from '@mui/material/Accordion';
21
import AccordionSummary from '@mui/material/AccordionSummary';
22
import AccordionDetails from '@mui/material/AccordionDetails';
23
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
24

25
import Box from "@mui/material/Box";
26
import Backdrop from "@mui/material/Backdrop";
27
import CircularProgress from "@mui/material/CircularProgress";
28
import InputLabel from "@mui/material/InputLabel";
29
import Item from "@mui/material/Grid";
30
import MenuItem from "@mui/material/MenuItem";
31
import Select from "@mui/material/Select";
32
import Snackbar from "@mui/material/Snackbar";
33
import Alert from "@mui/material/Alert";
34
import axios from "axios";
35
import axiosRetry from "axios-retry";
36

37
function App() {
38
  if (process.env.NODE_ENV === "production") {
4,092!
39
    console.log = function () {};
×
40
    console.debug = function () {};
×
41
    console.info = function () {};
×
42
    console.warn = console.error;
×
43
  }
44

45
  const apiUrl = process.env.REACT_APP_API_URL;
4,092✔
46
  const [loading, setLoading] = useState(true);
4,092✔
47
  const [inProgress, setInProgress] = useState(false);
3,768✔
48
  const [endpoints, setEndpoints] = useState(null);
3,768✔
49
  const [groupedEndpoints, setGroupedEndpoints] = useState({});
3,768✔
50
  const [openCategory, setOpenCategory] = useState(null);
3,768✔
51
  const [selectedEndpoint, setSelectedEndpoint] = useState(null);
3,768✔
52
  const [selectedModel, setSelectedModel] = useState(null);
3,768✔
53
  const [selectedFileExtension, setSelectedFileExtension] = useState(null);
3,768✔
54
  const [formOptions, setFormOptions] = useState({});
3,768✔
55
  const [downloadUrl, setDownloadUrl] = useState(null);
3,768✔
56
  const [models, setModels] = useState(null);
3,768✔
57
  const [filename, setFilename] = useState(null);
3,768✔
58
  const [showError, setShowError] = useState(false);
3,768✔
59
  const [errorMessage, setErrorMessage] = useState(null);
3,768✔
60
  const multilines = [
3,768✔
61
    "content",
62
    "data_columns",
63
    "options",
64
    "mp3_generator_kwargs",
65
    "pdf_generator_kwargs",
66
    "image_generator_kwargs",
67
  ]; // Multi-line fields
68

69
  axiosRetry(axios, {
3,768✔
70
    retries: 20,
71
    retryDelay: (retryCount) => {
72
      console.log("retryCount");
×
73
      console.log(retryCount);
×
74
      return retryCount * 1000;
×
75
    },
76
  });
77

78
  useEffect(() => {
3,768✔
79
    const fetchEndpoints = async () => {
324✔
80
      const response = await axios.get(`${apiUrl}/openapi.json`, {
324✔
81
        retry: {
82
          retries: 20,
83
        },
84
      });
85
      const schema = response.data;
324✔
86
      const paths = schema.paths;
324✔
87
      let grouped = {};
324✔
88
      const endpoints = Object.keys(paths).reduce((acc, path) => {
324✔
89
        const endpoint = path.split("/")[1];
9,396✔
90
        if (endpoint !== "heartbeat" && endpoint !== "providers") {
9,396✔
91
          acc[endpoint] = paths[path].post.requestBody.content["application/json"].schema;
8,748✔
92
          const tags = paths[path].post?.tags || ["Uncategorized"];
8,748!
93
          tags.forEach((tag) => {
8,748✔
94
            if (!grouped[tag]) {
8,748✔
95
              grouped[tag] = {};
3,564✔
96
            }
97
            grouped[tag][path] = endpoint;
8,748✔
98
          });
99
        }
100
        console.log("grouped");
9,396✔
101
        console.log(grouped);
9,396✔
102
        return acc;
9,396✔
103
      }, {});
104
      setEndpoints(endpoints);
324✔
105
      console.log("grouped (outside)");
324✔
106
      console.log(grouped);
324✔
107
      setGroupedEndpoints(grouped);
324✔
108
      console.log("ENDPOINTS");
324✔
109
      console.log(endpoints);
324✔
110

111
      const models = schema.components.schemas;
324✔
112
      setModels(models);
324✔
113
      setLoading(false);
324✔
114
      console.log("MODELS");
324✔
115
      console.log(models);
324✔
116
      // console.log("groupedEndpoints");
117
      // console.log(groupedEndpoints);
118
    };
119
    fetchEndpoints();
324✔
120
  }, []);
121

122
  useEffect(() => {
3,768✔
123
    if (endpoints && selectedEndpoint) {
972✔
124
      console.log(endpoints[selectedEndpoint]);
324✔
125
      setFormOptions(
324✔
126
        Object.fromEntries(
127
          Object.entries(endpoints[selectedEndpoint].properties || {}).map(
324✔
128
            ([name, property]) => [name, property.default]
×
129
          )
130
        )
131
      );
132
    }
133
  }, [endpoints, selectedEndpoint]);
134

135
  const handleEndpointClick = (endpoint) => {
3,768✔
136
    console.log(endpoint);
324✔
137
    setSelectedEndpoint(endpoint);
324✔
138
    setSelectedFileExtension(endpoint.slice(0, endpoint.indexOf("_file")));
324✔
139
    setSelectedModel(`${endpoint}_model`);
324✔
140
    console.log(`model: ${endpoint}_model`);
324✔
141
    console.log(models[`${endpoint}_model`]);
324✔
142
    setFormOptions(models[`${endpoint}_model`].properties || {});
324!
143
    setDownloadUrl(null);
324✔
144
    setFilename(null);
324✔
145
  };
146

147
  const handleOptionChange = (event) => {
3,768✔
148
    const { name, value, checked } = event.target;
1,752✔
149
    console.log(models[selectedModel].properties);
1,752✔
150
    console.log(name);
1,752✔
151
    console.log(models[selectedModel].properties[name]);
1,752✔
152
    console.log("checked");
1,752✔
153
    console.log(checked);
1,752✔
154
    console.log("value", value);
1,752✔
155
    let valueType = models[selectedModel].properties[name]?.type;
1,752✔
156
    setFormOptions((prevOptions) => ({
1,752✔
157
      ...prevOptions,
158
      [name]:
159
        valueType === "boolean"
876!
160
          ? checked
161
          : value,
162
    }));
163
  };
164

165
  const handleCategoryClick = (category) => {
3,768✔
166
    if (openCategory === category) {
324!
167
      setOpenCategory(null);
×
168
    } else {
169
      setOpenCategory(category);
324✔
170
    }
171
  };
172

173
  const handleSubmit = async () => {
3,768✔
174
    try {
162✔
175
      setInProgress(true);
162✔
176
      const body = JSON.stringify(
162✔
177
        Object.fromEntries(
178
          Object.entries(formOptions).map(([name, value]) => {
179
            if (["data_columns"].includes(name) && typeof value === "string") {
342✔
180
              if (["csv_file"].includes(selectedModel)) {
6!
181
                value = value.split(",").map((str) => str.trim());
×
182
              } else {
183
                value = JSON.parse(value);
6✔
184
              }
185
            } else if (
336!
186
              [
168!
187
                "options",
188
                "mp3_generator_kwargs",
189
                "pdf_generator_kwargs",
190
                "image_generator_kwargs",
191
              ].includes(name) &&
192
              typeof value === "string" &&
193
              value.trim() !== ""
194
            ) {
195
              try {
×
196
                value = JSON.parse(value);
×
197
              } catch (e) {
198
                console.log("invalid value");
×
199
                console.log(value);
×
200
                value = null;
×
201
              }
202
            } else if (["size"].includes(name) && typeof value === "string" && value.trim() !== "") {
336!
203
              value = value.split(",");
×
204
            }
205
            console.log("name");
342✔
206
            console.log(name);
342✔
207
            console.log("value");
342✔
208
            console.log(value);
342✔
209
            if (value && value.constructor === String && value.trim() === "") {
342!
210
              value = null;
×
211
            }
212
            return [name, value];
342✔
213
          })
214
        )
215
      );
216
      console.log("body");
162✔
217
      console.log(body);
162✔
218
      const response = await fetch(`${apiUrl}/${selectedEndpoint}/`, {
162✔
219
        method: "POST",
220
        headers: {
221
          "Content-Type": "application/json",
222
        },
223
        body: body,
224
      });
225

226
      if (!response.ok) {
162!
227
        const errorResponse = await response.json();
×
228
        console.error("Server responded with an error:", errorResponse);
×
229
        // If errorResponse contains a message, use it. Otherwise use a default message
230
        const _errorMessage = errorResponse.message || "An error occurred";
×
231
        // Handle the error based on errorResponse here
232
        setInProgress(false);
×
233
        setErrorMessage(_errorMessage);
×
234
        setShowError(true);
×
235
        return;
×
236
      }
237

238
      const blob = await response.blob();
162✔
239
      console.log("response");
162✔
240
      console.log(response);
162✔
241
      const downloadUrl = window.URL.createObjectURL(blob);
162✔
242
      setDownloadUrl(downloadUrl);
162✔
243
      setInProgress(false);
162✔
244

245
      // Get the content-disposition header
246
      const contentDisposition = response.headers.get("content-disposition");
162✔
247
      console.log(contentDisposition);
162✔
248

249
      // Extract the 'filename' value
250
      const match = /filename=([^;]+)/.exec(contentDisposition);
162✔
251
      console.log(match);
162✔
252

253
      let filename;
254
      if (match && match.length > 1) {
162!
255
        filename = match[1];
162✔
256
        setFilename(filename);
162✔
257
      } else {
258
        setFilename(`${selectedEndpoint}.${selectedFileExtension}`);
×
259
      }
260
      console.log("filename");
162✔
261
      console.log(filename);
162✔
262
    } catch (err) {
263
      console.log("An error occurred:");
×
264
      console.log(err);
×
265
      console.error("An error occurred:", err);
×
266
      // If err object contains a message, use it. Otherwise use a default message
267
      const _errorMessage = err.message || "An error occurred";
×
268

269
      // Handle the error here
270
      setInProgress(false);
×
271
      setErrorMessage(_errorMessage);
×
272
      setShowError(true);
×
273
    }
274
  };
275

276
  const theme = createTheme();
3,768✔
277

278
  if (loading) {
3,768✔
279
    return (
324✔
280
      <ThemeProvider theme={theme}>
281
        <Box
282
          sx={{
283
            display: "flex",
284
            justifyContent: "center",
285
            alignItems: "center",
286
          }}
287
        >
288
          <CircularProgress size={200} />
289
        </Box>
290
      </ThemeProvider>
291
    );
292
  }
293
  console.log("groupedEndpoints (just before)");
3,444✔
294
  console.log(groupedEndpoints);
3,444✔
295
  return (
3,444✔
296
    <ThemeProvider theme={theme}>
297
      <Grid
298
        container
299
        spacing={0}
300
        columns={12}
301
        sx={{
302
          "--Grid-borderWidth": "1px",
303
          borderTop: "var(--Grid-borderWidth) solid",
304
          borderLeft: "var(--Grid-borderWidth) solid",
305
          borderColor: "divider",
306
          "& > div": {
307
            borderRight: "var(--Grid-borderWidth) solid",
308
            borderBottom: "var(--Grid-borderWidth) solid",
309
            borderColor: "divider",
310
          },
311
        }}
312
      >
313

314
        {/*<Grid item xs={2}>*/}
315
        {/*  <Item sx={{ py: 2, px: 2 }}>*/}
316
        {/*    <Typography variant="h4" component="h1" gutterBottom>*/}
317
        {/*      File type*/}
318
        {/*    </Typography>*/}
319
        {/*    <List component="nav" aria-label="main providers folders">*/}
320
        {/*      {endpoints &&*/}
321
        {/*        Object.keys(endpoints).map((endpoint) => (*/}
322
        {/*          <ListItemButton*/}
323
        {/*            key={endpoint}*/}
324
        {/*            selected={selectedEndpoint === endpoint}*/}
325
        {/*            onClick={() => handleEndpointClick(endpoint)}*/}
326
        {/*          >*/}
327
        {/*            <ListItemText primary={endpoint.split("_file")[0].replace(/_/g, " ")} />*/}
328
        {/*          </ListItemButton>*/}
329
        {/*        ))}*/}
330
        {/*    </List>*/}
331
        {/*  </Item>*/}
332
        {/*</Grid>*/}
333

334
        <Grid item xs={2}>
335
          <Item sx={{ py: 0, px: 0 }}>
336
            <Typography variant="h4" component="h1" gutterBottom  sx={{ pt: 2, pl: 2, pb: 0, pr: 0 }}>
337
              File type
338
            </Typography>
339
            <List component="nav" aria-label="main providers folders">
340
              {groupedEndpoints &&
3,444✔
341
                Object.keys(groupedEndpoints).map((category) => (
342
                  <>
37,884✔
343
                    <ListItemButton
344
                      key={category}
345
                      onClick={() => handleCategoryClick(category)}
324✔
346
                      className="category"
347
                    >
348
                      <ListItemText primary={category}/>
349
                    </ListItemButton>
350
                    <Collapse in={openCategory === category} timeout="auto" unmountOnExit>
351
                      <List component="div" disablePadding>
352
                        {Object.keys(groupedEndpoints[category]).map((endpointPath) => {
353
                          const endpoint = groupedEndpoints[category][endpointPath];
92,988✔
354
                          return (
92,988✔
355
                            <ListItemButton
356
                              key={endpoint}
357
                              sx={{ pl: 4, pr: 0, pb: 0, pt: 0 }}
358
                              selected={selectedEndpoint === endpoint}
359
                              onClick={() => handleEndpointClick(endpoint)}
324✔
360
                              className="subcategory"
361
                            >
362
                              <ListItemText primary={endpoint.split("_file")[0].replace(/_/g, " ")} />
363
                            </ListItemButton>
364
                          );
365
                        })}
366
                      </List>
367
                    </Collapse>
368
                  </>
369
                ))}
370
            </List>
371
          </Item>
372
        </Grid>
373

374
        <Grid item xs={8}>
375
          <Item sx={{ py: 2, px: 2 }}>
376
            <Typography variant="h4" component="h1" gutterBottom>
377
              Options
378
            </Typography>
379
            {selectedEndpoint && endpoints && (
4,518✔
380
              <div>
381
                <form>
382
                  {Object.entries(models[selectedModel].properties || {}).map(
1,398!
383
                    ([name, property]) => {
384
                      const inputProps = {
16,686✔
385
                        style: { height: "auto" },
386
                      };
387
                      const props = multilines.includes(name)
16,686✔
388
                        ? { multiline: true, rows: 4, maxRows: 20, inputProps }
389
                        : {};
390
                      const key = `${selectedModel}_${name}`;
16,686✔
391
                      if (property?.type === "boolean") {
16,686✔
392
                        return (
72✔
393
                          <FormControlLabel
394
                            key={key}
395
                            control={
396
                              <Checkbox
397
                                name={name}
398
                                label={property.title || name}
36!
399
                                checked={
400
                                  formOptions[name] || property.default || false
96✔
401
                                }
402
                                margin="normal"
403
                                variant="outlined"
404
                                onChange={handleOptionChange}
405
                                {...props}
406
                              />
407
                            }
408
                            label={property.title || name}
36!
409
                          />
410
                        );
411
                      } else if (property?.allOf) {
16,614✔
412
                        let defaultValue;
413
                        let enumModel;
414
                        let label;
415
                        for (const condition of property.allOf) {
576✔
416
                          if (condition.$ref) {
576!
417
                            const modelName = condition.$ref.split("/").pop();
576✔
418
                            enumModel = models[modelName]?.enum;
576✔
419
                            label = models[modelName]?.title || name;
576!
420
                            if (enumModel) break;
576!
421
                          }
422
                        }
423
                        // console.log("enumModel", enumModel);
424
                        defaultValue = property?.default || enumModel?.[0] || "";
576!
425
                        // Initialize the state if it's not set
426
                        if (formOptions[name] === undefined) {
576✔
427
                          setFormOptions(prevOptions => ({
72✔
428
                            ...prevOptions,
429
                            [name]: defaultValue,
430
                          }));
431
                        }
432
                        return (
576✔
433
                          <FormControl key={key} sx={{ m: 0, mt: 2, mb: 2, width: '100%' }}>
434
                            <InputLabel htmlFor={name}>{label}</InputLabel>
435
                            <Select
436
                              labelId={name}
437
                              id={name}
438
                              name={name}
439
                              value={formOptions[name] || defaultValue}
324✔
440
                              onChange={handleOptionChange}
441
                              margin="normal"
442
                              variant="outlined"
443
                            >
444
                              {enumModel?.map((option, index) => (
445
                                <MenuItem key={index} value={option}>
1,254✔
446
                                  {option}
447
                                </MenuItem>
448
                              ))}
449
                            </Select>
450
                          </FormControl>
451
                        );
452
                      } else {
453
                        return (
16,038✔
454
                          <TextField
455
                            key={key}
456
                            name={name}
457
                            label={property.title || name}
8,019!
458
                            defaultValue={property.default || null}
14,271✔
459
                            fullWidth
460
                            margin="normal"
461
                            variant="outlined"
462
                            onChange={handleOptionChange}
463
                            {...props}
464
                          />
465
                        );
466
                      }
467
                    }
468
                  )}
469
                  <Button
470
                    variant="contained"
471
                    color="primary"
472
                    onClick={handleSubmit}
473
                  >
474
                    Generate
475
                  </Button>
476
                  <Snackbar
477
                    anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
478
                    open={showError}
479
                    autoHideDuration={5000}
480
                    onClose={() => setShowError(false)}
×
481
                  >
482
                    <Alert severity="error">{errorMessage}</Alert>
483
                  </Snackbar>
484
                </form>
485

486
                <Backdrop
487
                  sx={{
488
                    color: "#fff",
489
                    zIndex: (theme) => theme.zIndex.drawer + 1,
3,048✔
490
                  }}
491
                  open={inProgress}
492
                >
493
                  <CircularProgress color="inherit" />
494
                </Backdrop>
495
              </div>
496
            )}
497
          </Item>
498
        </Grid>
499
        <Grid item xs={2}>
500
          <Item sx={{ py: 2, px: 2 }}>
501
            <Typography variant="h4" component="h1" gutterBottom>
502
              Result
503
            </Typography>
504
            {downloadUrl && (
1,803✔
505
              <Link href={downloadUrl} download={`${filename}`}>
506
                Download
507
              </Link>
508
            )}
509
          </Item>
510
        </Grid>
511
      </Grid>
512
    </ThemeProvider>
513
  );
514
}
515

516
export default App;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc