Skip to content

Commit

Permalink
feat: give user option to generate primary keys or use existing prima…
Browse files Browse the repository at this point in the history
…ry keys defined in worksheet
  • Loading branch information
Jmurp11 committed Sep 4, 2021
1 parent 1ab0f72 commit 3e58cb9
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 20 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ Supports two options, both of which are optional:

* *createDatabase* - _true | false_ (Defaults to false)
* *createTables* - _true | false_ (Defaults to false)
* *useExistingPrimaryKeys* - _true | false_ (Defaults to false. Supports multiple primary keys. Append '_pk' to the column name in the workbook that will be the primary key)
* *generatePrimaryKey* - _true | false_ (Defaults to false. Generates 'id' column to be used as a primary key. Cannot be used with 'useExistingPrimaryKeys' option)
* *useExistingPrimaryKeys* - _true | false_ (Defaults to false. Supports multiple primary keys. Append '_pk' to the column name in the workbook that will be the primary key. Cannot be used with 'generatePrimaryKey' option)
# Testing

This package's tests are written using [Jest](https://jestjs.io/). To execute, run:
Expand Down
12 changes: 11 additions & 1 deletion src/etl-processes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export enum SQLType {
}

export enum SQLKeyword {
PRIMARY_KEY = 'PRIMARY KEY'
PRIMARY_KEY = 'PRIMARY KEY',
NOT_NULL = 'NOT NULL',
SERIAL = 'SERIAL'
}
export interface Column {
name: string;
Expand Down Expand Up @@ -113,5 +115,13 @@ export function formatPrimaryKey(formatColumnsResult: FormatColumnsResult): stri

formatColumnsResult.formattedColumns.push(primaryColumns);

return formatColumnsResult.formattedColumns;
}

export function generatePrimaryKey(formatColumnsResult: FormatColumnsResult): string[] {
const primaryColumn = `id ${SQLType.INT} ${SQLKeyword.PRIMARY_KEY} ${SQLKeyword.NOT_NULL} ${SQLKeyword.SERIAL}`;

formatColumnsResult.formattedColumns.push(primaryColumn);

return formatColumnsResult.formattedColumns;
}
57 changes: 45 additions & 12 deletions src/sql.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,47 @@
import { Pool } from 'pg';
import { getFields, Fields, Column, getColumns, formatColumns, formatPrimaryKey } from './etl-processes';
import { getFields, Fields, Column, getColumns, formatColumns, formatPrimaryKey, generatePrimaryKey } from './etl-processes';
import { readExcel } from './excel';

export enum PrimaryKeyOptions {
GENERATE = 'GENERATE',
USE_EXISTING = 'USE EXISTING',
NO_PRIMARY_KEY = 'NO PRIMARY KEY'
}

export interface Connection {
user: string;
host: string;
database: string;
password: string;
port: number;
user: string;
host: string;
database: string;
password: string;
port: number;
}

export interface Options {
createDatabase?: boolean;
createTables?: boolean;
createDatabase?: boolean;
createTables?: boolean;
generatePrimaryKey?: boolean;
useExistingPrimaryKeys?: boolean;
}

export function createDatabase(dbName: string): string {
return `CREATE DATABASE ${dbName};`;
}

export function createTable<T>(tableName: string, data: T, useExistingPrimaryKeys?: boolean): string {
export function createTable<T>(tableName: string, data: T, primaryKeyOptions: string): string {
const fields: Fields<T> = getFields(data);
const columns: Column[] = getColumns(fields);
const formattedColumns: string[] = useExistingPrimaryKeys ? formatPrimaryKey(formatColumns(columns)) : formatColumns(columns).formattedColumns;

let formattedColumns: string[] = [];

if (primaryKeyOptions === PrimaryKeyOptions.GENERATE) {
formattedColumns = generatePrimaryKey(formatColumns(columns));;
} else if (primaryKeyOptions === PrimaryKeyOptions.USE_EXISTING) {
formattedColumns = formatPrimaryKey(formatColumns(columns));
} else if (primaryKeyOptions === PrimaryKeyOptions.NO_PRIMARY_KEY) {
formattedColumns = formatColumns(columns).formattedColumns;
} else {
return;
}

return `CREATE TABLE ${tableName.replace(/\s/g, '')} (
${formattedColumns}
Expand Down Expand Up @@ -71,14 +89,29 @@ async function executeQuery(connectionInfo: Connection, query: string) {
await pool.end();
}

export function handlePrimaryKey(options: Options): string {
if (options.generatePrimaryKey) {
return PrimaryKeyOptions.GENERATE;
} else if (options.useExistingPrimaryKeys) {
return PrimaryKeyOptions.USE_EXISTING;
} else {
return PrimaryKeyOptions.NO_PRIMARY_KEY;
}
}

export async function excelToPostgresDb(connectionInfo: Connection, filePath: string, options?: Options): Promise<void> {

if (options?.generatePrimaryKey && options?.useExistingPrimaryKeys) {
return console.error('Cannot generate primary keys column and also use existing primary keys');
}

const sheets = readExcel(filePath);

let insertQuery = '';
let tableQuery = '';

sheets.forEach(async (sheet) => {
tableQuery = tableQuery.concat(createTable(sheet.title, sheet.data[0], options?.useExistingPrimaryKeys));
tableQuery = tableQuery.concat(createTable(sheet.title, sheet.data[0], handlePrimaryKey(options)));
insertQuery = insertQuery.concat(insert(sheet.title, sheet.data));
});

Expand Down
20 changes: 15 additions & 5 deletions src/tests/sql.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import { createDatabase, createTable, insert } from '../sql';
import { createDatabase, createTable, handlePrimaryKey, insert, PrimaryKeyOptions } from '../sql';
import { etlProcesses, sqlInfo, sqlResults } from './test-data';

describe('SQL tests', () => {
test('create database', () => {
expect(createDatabase(sqlInfo.database)).toEqual(sqlResults.createDatabase);
});

test('create table one primary key', () => {
expect(createTable(sqlInfo.tableName, etlProcesses.sheet_one_pk, true)).toEqual(sqlResults.createTableOnePrimaryKey);
test('create table generate primary key', () => {
expect(createTable(sqlInfo.tableName, etlProcesses.sheet_one_pk, PrimaryKeyOptions.GENERATE)).toEqual(sqlResults.createTableGeneratedPrimaryKey);
});

test('create table multiple primary key', () => {
expect(createTable(sqlInfo.tableName, etlProcesses.sheet_multiple_pk, true)).toEqual(sqlResults.createTableMultiplePrimaryKeys);
test('create table one existing primary key', () => {
expect(createTable(sqlInfo.tableName, etlProcesses.sheet_one_pk, PrimaryKeyOptions.USE_EXISTING)).toEqual(sqlResults.createTableOnePrimaryKey);
});

test('create table multiple existing primary keys', () => {
expect(createTable(sqlInfo.tableName, etlProcesses.sheet_multiple_pk, PrimaryKeyOptions.USE_EXISTING)).toEqual(sqlResults.createTableMultiplePrimaryKeys);
});

test('insert', () => {
expect(insert(sqlInfo.tableName, [etlProcesses.sheet_one_pk])).toEqual(sqlResults.insert);
});

test('handle primary key options', () => {
expect(handlePrimaryKey({ generatePrimaryKey: true })).toEqual(PrimaryKeyOptions.GENERATE);
expect(handlePrimaryKey({ useExistingPrimaryKeys: true })).toEqual(PrimaryKeyOptions.USE_EXISTING);
expect(handlePrimaryKey({ })).toEqual(PrimaryKeyOptions.NO_PRIMARY_KEY);
});
});
11 changes: 10 additions & 1 deletion src/tests/test-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,13 @@ export const etlProcesses = {
`${columns_multiple_pk[1].name} ${columns_multiple_pk[1].type}`,
`${columns_multiple_pk[2].name} ${columns_multiple_pk[2].type}`,
`${SQLKeyword.PRIMARY_KEY} (${columns_multiple_pk[0].name},${columns_multiple_pk[1].name})`
]
],
formattedColumnsGeneratedPrimaryKey: [
`${columns_one_pk[0].name} ${columns_one_pk[0].type}`,
`${columns_one_pk[1].name} ${columns_one_pk[1].type}`,
`${columns_one_pk[2].name} ${columns_one_pk[2].type}`,
`id ${SQLType.INT} ${SQLKeyword.PRIMARY_KEY} ${SQLKeyword.NOT_NULL} ${SQLKeyword.SERIAL}`
],
};

export const sqlInfo = {
Expand All @@ -109,6 +115,9 @@ export const sqlInfo = {

export const sqlResults = {
createDatabase: `CREATE DATABASE ${sqlInfo.database};`,
createTableGeneratedPrimaryKey: `CREATE TABLE ${sqlInfo.tableName} (
${etlProcesses.formattedColumnsGeneratedPrimaryKey}
);`,
createTableOnePrimaryKey: `CREATE TABLE ${sqlInfo.tableName} (
${etlProcesses.formattedColumnsOnePrimaryKey}
);`,
Expand Down

0 comments on commit 3e58cb9

Please sign in to comment.