diff --git a/README.md b/README.md index 56b9b47..2f5ec42 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/src/etl-processes.ts b/src/etl-processes.ts index 47f3fe2..808c97d 100644 --- a/src/etl-processes.ts +++ b/src/etl-processes.ts @@ -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; @@ -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; } \ No newline at end of file diff --git a/src/sql.ts b/src/sql.ts index 515216e..d3860bd 100644 --- a/src/sql.ts +++ b/src/sql.ts @@ -1,18 +1,25 @@ 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; } @@ -20,10 +27,21 @@ export function createDatabase(dbName: string): string { return `CREATE DATABASE ${dbName};`; } -export function createTable(tableName: string, data: T, useExistingPrimaryKeys?: boolean): string { +export function createTable(tableName: string, data: T, primaryKeyOptions: string): string { const fields: Fields = 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} @@ -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 { + + 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)); }); diff --git a/src/tests/sql.test.ts b/src/tests/sql.test.ts index 47b88a0..ba93f97 100644 --- a/src/tests/sql.test.ts +++ b/src/tests/sql.test.ts @@ -1,4 +1,4 @@ -import { createDatabase, createTable, insert } from '../sql'; +import { createDatabase, createTable, handlePrimaryKey, insert, PrimaryKeyOptions } from '../sql'; import { etlProcesses, sqlInfo, sqlResults } from './test-data'; describe('SQL tests', () => { @@ -6,15 +6,25 @@ describe('SQL tests', () => { 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); + }); }); \ No newline at end of file diff --git a/src/tests/test-data.ts b/src/tests/test-data.ts index 9ef50e0..1da6e87 100644 --- a/src/tests/test-data.ts +++ b/src/tests/test-data.ts @@ -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 = { @@ -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} );`,