Skip to content

Commit

Permalink
Finish version 1.7.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Chacaponquin committed Dec 30, 2023
1 parent c429d70 commit 9f3cceb
Show file tree
Hide file tree
Showing 4 changed files with 394 additions and 1 deletion.
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { terser } from "rollup-plugin-terser";
// import { terser } from "rollup-plugin-terser";
import pluginTypescript from "@rollup/plugin-typescript";
import pluginCommonjs from "@rollup/plugin-commonjs";
import pluginNodeResolve from "@rollup/plugin-node-resolve";
Expand Down
111 changes: 111 additions & 0 deletions src/core/Export/generators/Csv/CsvGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Generator } from "../Generator/Generator";
import fs from "fs";
import path from "path";
import { ChacaError } from "../../../../errors";
import { MultiGenerateResolver } from "../../../MultiGenerate/MultiGenerateResolver";
import { CSVArray, CSVDataType, CSVObject } from "./core/types";

interface Props {
fileName: string;
location: string;
zip?: boolean;
}

export class CsvGenerator extends Generator {
private zip: boolean;

constructor(config: Props) {
super({
extension: "csv",
fileName: config.fileName,
location: config.location,
});

this.zip = Boolean(config.zip);
}

public async generateRelationalDataFile(
resolver: MultiGenerateResolver,
): Promise<string> {
const allResolvers = resolver.getResolvers();

if (allResolvers.length === 1) {
const schemaData = allResolvers[0].resolve();
const route = await this.createFile(this.fileName, schemaData);

return route;
} else {
const allRoutes = [] as Array<string>;

for (const r of allResolvers) {
const schemaData = r.resolve();
const route = await this.createFile(r.getSchemaName(), schemaData);
allRoutes.push(route);
}

if (this.zip) {
const { zip, zipPath } = this.createZip();

for (const route of allRoutes) {
zip.addLocalFile(route);
await fs.promises.unlink(route);
}

zip.writeZip(zipPath);

return zipPath;
} else {
return this.baseLocation;
}
}
}

public async generateFile(data: any): Promise<string> {
const fileRoute = await this.createFile(this.fileName, data);

if (this.zip) {
const { zip, zipPath } = this.createZip();

zip.addLocalFile(fileRoute);
zip.writeZip(zipPath);

await fs.promises.unlink(fileRoute);

return zipPath;
} else {
return fileRoute;
}
}

private async createFile(filename: string, data: any): Promise<string> {
const fileRoute = path.join(this.baseLocation, `${filename}.csv`);

const dataType = CSVDataType.filterTypeByValue(data);

let content = "";

// array
if (dataType instanceof CSVArray) {
content = dataType.getCSVValue();
}

// object
else if (dataType instanceof CSVObject) {
const array = [dataType.object];
const newArrayType = new CSVArray(array);

content = newArrayType.getCSVValue();
}

// other
else {
throw new ChacaError(
`Your export data must be an array of objects or a single object.`,
);
}

await fs.promises.writeFile(fileRoute, content, "utf-8");

return fileRoute;
}
}
205 changes: 205 additions & 0 deletions src/core/Export/generators/Csv/core/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import { ChacaError } from "../../../../../errors";

interface ObjectKey {
key: string;
type: CSVDataType;
}

export abstract class CSVDataType {
public abstract getCSVValue(): string;

public static filterTypeByValue(value: any): CSVDataType {
let type: CSVDataType = new CSVNull();

if (typeof value === "string") {
type = new CSVString(value);
} else if (typeof value === "number") {
type = new CSVNumber(value);
} else if (typeof value === "boolean") {
type = new CSVBoolean(value);
} else if (typeof value === "bigint") {
type = new CSVBigint(value);
} else if (typeof value === "undefined") {
type = new CSVNull();
} else if (typeof value === "function") {
throw new ChacaError(`You can not export a function in a csv file.`);
} else if (typeof value === "symbol") {
throw new ChacaError(`You can not export a Symbol in a csv file.`);
} else if (typeof value === "object") {
if (value instanceof Date) {
type = new CSVDate(value);
} else if (Array.isArray(value)) {
type = new CSVArray(value);
} else if (value === null) {
type = new CSVNull();
} else {
type = new CSVObject(value);
}
}

return type;
}
}

export class CSVBoolean extends CSVDataType {
constructor(private readonly value: boolean) {
super();
}

public getCSVValue(): string {
if (this.value) return "TRUE";
else return "FALSE";
}
}

export class CSVDate extends CSVDataType {
constructor(private readonly value: Date) {
super();
}

public getCSVValue(): string {
return this.value.toISOString();
}
}

export class CSVNull extends CSVDataType {
public getCSVValue(): string {
return "NULL";
}
}

export class CSVString extends CSVDataType {
constructor(private readonly value: string) {
super();
}

public getCSVValue(): string {
return JSON.stringify(this.value);
}
}

export class CSVBigint extends CSVDataType {
constructor(private readonly value: bigint) {
super();
}

public getCSVValue(): string {
return this.value.toString();
}
}

export class CSVNumber extends CSVDataType {
constructor(private readonly value: number) {
super();
}

public getCSVValue(): string {
let retString: string;

if (Number.isNaN(this.value)) {
retString = "NaN";
} else if (this.value === Infinity) {
retString = "Infinity";
} else if (this.value === -Infinity) {
retString = "-Infinity";
} else {
retString = `${this.value}`;
}

return retString;
}
}

export class CSVArray extends CSVDataType {
private values: Array<CSVObject> = [];

constructor(array: Array<any>) {
super();
for (const v of array) {
const type = CSVDataType.filterTypeByValue(v);

if (
type instanceof CSVObject &&
this.values.every((v) => type.equal(v))
) {
this.values.push(type);
} else {
throw new ChacaError(`Your objects array must have the same keys`);
}
}
}

public getValues() {
return this.values;
}

public getCSVValue(): string {
let content = "";

if (this.values.length > 0) {
const firstObject = this.values[0];
const allKeys = firstObject.getKeys();

const header = allKeys.join(",") + "\n";

let allObjectsContent = "";
for (const o of this.values) {
allObjectsContent += o.getCSVValue();
}

content = header + allObjectsContent;
}

return content;
}
}

export class CSVObject extends CSVDataType {
private keys: Array<ObjectKey> = [];

constructor(public readonly object: any) {
super();

for (const [key, value] of Object.entries(object)) {
const type = CSVDataType.filterTypeByValue(value);

if (type instanceof CSVArray) {
throw new ChacaError(`You can not insert an array into a CSV File`);
} else if (type instanceof CSVObject) {
throw new ChacaError(
`You can not insert a nested object into a CSV File`,
);
}

this.keys.push({ key, type });
}
}

public getCSVValue(): string {
const values = this.keys.map((k) => k.type.getCSVValue());
return values.join(", ") + "\n";
}

public equal(otherObject: CSVObject): boolean {
let equal = true;

if (this.keys.length === otherObject.keys.length) {
for (let i = 0; i < this.keys.length; i++) {
const key = this.keys[i];
const exists = otherObject.keys.some((k) => k.key === key.key);

if (!exists) {
equal = false;
}
}
} else {
equal = false;
}

return equal;
}

public getKeys() {
return this.keys.map((k) => k.key);
}
}
Loading

0 comments on commit 9f3cceb

Please sign in to comment.