-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c429d70
commit 9f3cceb
Showing
4 changed files
with
394 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.