Skip to content

Commit 7f66247

Browse files
committed
refactor: follow DRY principle in upload
1 parent 814ae4e commit 7f66247

1 file changed

Lines changed: 64 additions & 68 deletions

File tree

apps/web/src/utils/upload.ts

Lines changed: 64 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { Group } from '@opendatacapture/schemas/group';
77
import type { UnilingualInstrumentInfo } from '@opendatacapture/schemas/instrument';
88
import type { UploadInstrumentRecordsData } from '@opendatacapture/schemas/instrument-records';
99
import { encodeScopedSubjectId } from '@opendatacapture/subject-utils';
10-
import { mapValues, reject } from 'lodash-es';
10+
import { mapValues } from 'lodash-es';
1111
import { parse, unparse } from 'papaparse';
1212
import type { Merge } from 'type-fest';
1313
import { z as z3 } from 'zod/v3';
@@ -19,6 +19,50 @@ const INTERNAL_HEADERS_SAMPLE_DATA = ['string', 'yyyy-mm-dd'];
1919

2020
const SUBJECT_ID_REGEX = /^[^$\s]+$/;
2121

22+
function parseBooleanEntry(entry: string): boolean {
23+
if (entry.toLowerCase() === 'true') {
24+
return true;
25+
} else if (entry.toLowerCase() === 'false') {
26+
return false;
27+
}
28+
throw new UploadError({
29+
en: `Undecipherable Boolean Type: '${entry}'`,
30+
fr: `Type booléen indéchiffrable : '${entry}'`
31+
});
32+
}
33+
34+
function parseDateEntry(entry: string): Date {
35+
const date = new Date(entry);
36+
if (Number.isNaN(date.getTime())) {
37+
throw new UploadError({
38+
en: `Failed to parse date: '${entry}'`,
39+
fr: `Échec de l'analyse de la date : '${entry}'`
40+
});
41+
}
42+
return date;
43+
}
44+
45+
function parseNumberEntry(entry: string): number {
46+
if (isNumberLike(entry)) {
47+
return parseNumber(entry);
48+
}
49+
throw new UploadError({
50+
en: `Invalid number type: '${entry}'`,
51+
fr: `Type de nombre invalide : '${entry}'`
52+
});
53+
}
54+
55+
function parseSetEntry(entry: string): Set<string> {
56+
const set = extractSetEntry(entry);
57+
if (set.size === 0) {
58+
throw new UploadError({
59+
en: 'Empty set is not allowed',
60+
fr: "Un ensemble vide n'est pas autorisé"
61+
});
62+
}
63+
return set;
64+
}
65+
2266
const ZOD_TYPE_NAMES = [
2367
'ZodNumber',
2468
'ZodString',
@@ -333,7 +377,7 @@ export namespace Zod3 {
333377
}
334378
}
335379

336-
function interpetZodConvertResult(convertResult: ZodTypeNameResult, entry: string): unknown {
380+
function interpretZodConvertResult(convertResult: ZodTypeNameResult, entry: string): unknown {
337381
if (entry === '' && convertResult.isOptional) {
338382
return undefined;
339383
}
@@ -351,46 +395,22 @@ export namespace Zod3 {
351395
convertResult.multiKeys!.forEach((key, index) => {
352396
const rawValue = parsedRecord[key];
353397
if (rawValue !== undefined) {
354-
result[key] = interpetZodConvertResult(convertResult.multiValues![index]!, rawValue);
398+
result[key] = interpretZodConvertResult(convertResult.multiValues![index]!, rawValue);
355399
}
356400
});
357401
return result;
358402
});
359403

360404
case 'ZodBoolean':
361-
if (entry.toLowerCase() === 'true') {
362-
return true;
363-
} else if (entry.toLowerCase() === 'false') {
364-
return false;
365-
}
366-
throw new UploadError({
367-
en: `Undecipherable Boolean Type: '${entry}'`,
368-
fr: `Type booléen indéchiffrable : '${entry}'`
369-
});
370-
case 'ZodDate': {
371-
const date = new Date(entry);
372-
if (Number.isNaN(date.getTime())) {
373-
throw new UploadError({
374-
en: `Failed to parse date: '${entry}'`,
375-
fr: `Échec de l'analyse de la date : '${entry}'`
376-
});
377-
}
378-
return date;
379-
}
405+
return parseBooleanEntry(entry);
406+
case 'ZodDate':
407+
return parseDateEntry(entry);
380408
case 'ZodEnum':
381409
return entry;
382410
case 'ZodNumber':
383-
if (isNumberLike(entry)) {
384-
return parseNumber(entry);
385-
}
386-
throw new UploadError({ en: `Invalid number type: '${entry}'`, fr: `Type de nombre invalide : '${entry}'` });
387-
case 'ZodSet': {
388-
const set = extractSetEntry(entry);
389-
if (set.size === 0) {
390-
throw new UploadError({ en: 'Empty set is not allowed', fr: "Un ensemble vide n'est pas autorisé" });
391-
}
392-
return set;
393-
}
411+
return parseNumberEntry(entry);
412+
case 'ZodSet':
413+
return parseSetEntry(entry);
394414
case 'ZodString':
395415
return entry;
396416
default:
@@ -505,7 +525,7 @@ export namespace Zod3 {
505525
);
506526
}
507527
try {
508-
const result = interpetZodConvertResult(getZodTypeName(shape[key]), rawValue);
528+
const result = interpretZodConvertResult(getZodTypeName(shape[key]), rawValue);
509529
jsonLine[headers[i]!] = result;
510530
} catch (error: unknown) {
511531
if (error instanceof UploadError) {
@@ -655,51 +675,27 @@ export namespace Zod4 {
655675
}
656676
}
657677

658-
function interpetZodConvertResult(convertResult: ZodConvertResult, entry: string): unknown {
678+
function interpretZodConvertResult(convertResult: ZodConvertResult, entry: string): unknown {
659679
if (entry === '' && convertResult.isOptional) {
660680
return undefined;
661681
}
662682
switch (convertResult.typeName) {
663683
case 'array':
664684
return extractRecordArrayEntry(entry).map((parsedRecord) => {
665-
return mapValues(parsedRecord, (entry): unknown => interpetZodConvertResult(convertResult.innerType, entry));
685+
return mapValues(parsedRecord, (entry): unknown => interpretZodConvertResult(convertResult.innerType, entry));
666686
});
667687

668688
case 'boolean':
669-
if (entry.toLowerCase() === 'true') {
670-
return true;
671-
} else if (entry.toLowerCase() === 'false') {
672-
return false;
673-
}
674-
throw new UploadError({
675-
en: `Undecipherable Boolean Type: '${entry}'`,
676-
fr: `Type booléen indéchiffrable : '${entry}'`
677-
});
678-
case 'date': {
679-
const date = new Date(entry);
680-
if (Number.isNaN(date.getTime())) {
681-
throw new UploadError({
682-
en: `Failed to parse date: '${entry}'`,
683-
fr: `Échec de l'analyse de la date : '${entry}'`
684-
});
685-
}
686-
return date;
687-
}
689+
return parseBooleanEntry(entry);
690+
case 'date':
691+
return parseDateEntry(entry);
688692
case 'enum':
689693
return entry;
690694
case 'int':
691695
case 'number':
692-
if (isNumberLike(entry)) {
693-
return parseNumber(entry);
694-
}
695-
throw new UploadError({ en: `Invalid number type: '${entry}'`, fr: `Type de nombre invalide : '${entry}'` });
696-
case 'set': {
697-
const set = extractSetEntry(entry);
698-
if (set.size === 0) {
699-
throw new UploadError({ en: 'Empty set is not allowed', fr: "Un ensemble vide n'est pas autorisé" });
700-
}
701-
return set;
702-
}
696+
return parseNumberEntry(entry);
697+
case 'set':
698+
return parseSetEntry(entry);
703699
case 'string':
704700
return entry;
705701
default:
@@ -790,7 +786,7 @@ export namespace Zod4 {
790786

791787
const shape = instrumentSchema.shape as { [key: string]: z4.ZodType };
792788

793-
return new Promise<FormTypes.Data[]>((resolve) => {
789+
return new Promise<FormTypes.Data[]>((resolve, reject) => {
794790
const reader = new FileReader();
795791
reader.onload = () => {
796792
const text = reader.result as string;
@@ -837,7 +833,7 @@ export namespace Zod4 {
837833
);
838834
}
839835
try {
840-
const result = interpetZodConvertResult(parseZodSchema(shape[key]), rawValue);
836+
const result = interpretZodConvertResult(parseZodSchema(shape[key]), rawValue);
841837
jsonLine[headers[i]!] = result;
842838
} catch (error: unknown) {
843839
if (error instanceof UploadError) {

0 commit comments

Comments
 (0)