All files / if-run/builtins/csv-import index.ts

100% Statements 31/31
100% Branches 10/10
100% Functions 5/5
100% Lines 31/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94  4x     4x 4x   4x   4x             4x   4x   4x   13x 1x     12x               12x               12x 12x 10x   9x 15x     8x                   4x       15x 2x     13x   10x 8x   8x   18x 18x     8x     2x   2x         3x        
/* eslint-disable eqeqeq */
import {z} from 'zod';
 
import {ConfigParams, PluginParams} from '@grnsft/if-core/types';
import {PluginFactory} from '@grnsft/if-core/interfaces';
import {ERRORS, validate} from '@grnsft/if-core/utils';
 
import {STRINGS} from '../../config';
 
import {
  fieldAccessor,
  nanifyEmptyValues,
  parseCSVFile,
  retrieveFile,
} from '../util/csv-helpers';
 
const {MISSING_CONFIG} = STRINGS;
 
const {ConfigError} = ERRORS;
 
export const CSVImport = PluginFactory({
  configValidation: (config: ConfigParams) => {
    if (!config || !Object.keys(config)?.length) {
      throw new ConfigError(MISSING_CONFIG);
    }
 
    const configSchema = z.object({
      filepath: z.string(),
      output: z
        .string()
        .or(z.array(z.string()))
        .or(z.array(z.array(z.string()))),
    });
 
    return validate<z.infer<typeof configSchema>>(configSchema, config);
  },
  implementation: async (inputs: PluginParams[], config: ConfigParams) => {
    /**
     * 1. Tries to retrieve given file (with url or local path).
     * 2. Parses given CSV.
     * 3. Filters requested information from CSV.
     */
    const {filepath, output} = config;
    const file = await retrieveFile(filepath);
    const parsedCSV = parseCSVFile(file);
 
    const result = parsedCSV?.map((input: PluginParams) =>
      filterOutput(input, output)
    );
 
    return [...inputs, ...result];
  },
});
 
/**
 * 1. If output is anything, then removes query data from csv record to escape duplicates.
 * 2. Otherwise checks if it's a miltidimensional array, then grabs multiple fields ().
 * 3. If not, then returns single field.
 * 4. In case if it's string, then
 */
const filterOutput = (
  dataFromCSV: any,
  output: string | string[] | string[][]
) => {
  if (output === '*') {
    return nanifyEmptyValues(dataFromCSV);
  }
 
  if (Array.isArray(output)) {
    /** Check if it's a multidimensional array. */
    if (Array.isArray(output[0])) {
      const result: any = {};
 
      output.forEach(outputField => {
        /** Check if there is no renaming request, then export as is */
        const outputTitle = outputField[1] || outputField[0];
        result[outputTitle] = fieldAccessor(outputField[0], dataFromCSV);
      });
 
      return result;
    }
 
    const outputTitle = output[1] || output[0];
 
    return {
      [outputTitle as string]: fieldAccessor(output[0], dataFromCSV),
    };
  }
 
  return {
    [output]: fieldAccessor(output, dataFromCSV),
  };
};