Skip to content

Commit 3e58cb9

Browse files
committed
feat: give user option to generate primary keys or use existing primary keys defined in worksheet
1 parent 1ab0f72 commit 3e58cb9

File tree

5 files changed

+83
-20
lines changed

5 files changed

+83
-20
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ Supports two options, both of which are optional:
3434

3535
* *createDatabase* - _true | false_ (Defaults to false)
3636
* *createTables* - _true | false_ (Defaults to false)
37-
* *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)
37+
* *generatePrimaryKey* - _true | false_ (Defaults to false. Generates 'id' column to be used as a primary key. Cannot be used with 'useExistingPrimaryKeys' option)
38+
* *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)
3839
# Testing
3940

4041
This package's tests are written using [Jest](https://jestjs.io/). To execute, run:

src/etl-processes.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ export enum SQLType {
66
}
77

88
export enum SQLKeyword {
9-
PRIMARY_KEY = 'PRIMARY KEY'
9+
PRIMARY_KEY = 'PRIMARY KEY',
10+
NOT_NULL = 'NOT NULL',
11+
SERIAL = 'SERIAL'
1012
}
1113
export interface Column {
1214
name: string;
@@ -113,5 +115,13 @@ export function formatPrimaryKey(formatColumnsResult: FormatColumnsResult): stri
113115

114116
formatColumnsResult.formattedColumns.push(primaryColumns);
115117

118+
return formatColumnsResult.formattedColumns;
119+
}
120+
121+
export function generatePrimaryKey(formatColumnsResult: FormatColumnsResult): string[] {
122+
const primaryColumn = `id ${SQLType.INT} ${SQLKeyword.PRIMARY_KEY} ${SQLKeyword.NOT_NULL} ${SQLKeyword.SERIAL}`;
123+
124+
formatColumnsResult.formattedColumns.push(primaryColumn);
125+
116126
return formatColumnsResult.formattedColumns;
117127
}

src/sql.ts

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,47 @@
11
import { Pool } from 'pg';
2-
import { getFields, Fields, Column, getColumns, formatColumns, formatPrimaryKey } from './etl-processes';
2+
import { getFields, Fields, Column, getColumns, formatColumns, formatPrimaryKey, generatePrimaryKey } from './etl-processes';
33
import { readExcel } from './excel';
44

5+
export enum PrimaryKeyOptions {
6+
GENERATE = 'GENERATE',
7+
USE_EXISTING = 'USE EXISTING',
8+
NO_PRIMARY_KEY = 'NO PRIMARY KEY'
9+
}
10+
511
export interface Connection {
6-
user: string;
7-
host: string;
8-
database: string;
9-
password: string;
10-
port: number;
12+
user: string;
13+
host: string;
14+
database: string;
15+
password: string;
16+
port: number;
1117
}
1218

1319
export interface Options {
14-
createDatabase?: boolean;
15-
createTables?: boolean;
20+
createDatabase?: boolean;
21+
createTables?: boolean;
22+
generatePrimaryKey?: boolean;
1623
useExistingPrimaryKeys?: boolean;
1724
}
1825

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

23-
export function createTable<T>(tableName: string, data: T, useExistingPrimaryKeys?: boolean): string {
30+
export function createTable<T>(tableName: string, data: T, primaryKeyOptions: string): string {
2431
const fields: Fields<T> = getFields(data);
2532
const columns: Column[] = getColumns(fields);
26-
const formattedColumns: string[] = useExistingPrimaryKeys ? formatPrimaryKey(formatColumns(columns)) : formatColumns(columns).formattedColumns;
33+
34+
let formattedColumns: string[] = [];
35+
36+
if (primaryKeyOptions === PrimaryKeyOptions.GENERATE) {
37+
formattedColumns = generatePrimaryKey(formatColumns(columns));;
38+
} else if (primaryKeyOptions === PrimaryKeyOptions.USE_EXISTING) {
39+
formattedColumns = formatPrimaryKey(formatColumns(columns));
40+
} else if (primaryKeyOptions === PrimaryKeyOptions.NO_PRIMARY_KEY) {
41+
formattedColumns = formatColumns(columns).formattedColumns;
42+
} else {
43+
return;
44+
}
2745

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

92+
export function handlePrimaryKey(options: Options): string {
93+
if (options.generatePrimaryKey) {
94+
return PrimaryKeyOptions.GENERATE;
95+
} else if (options.useExistingPrimaryKeys) {
96+
return PrimaryKeyOptions.USE_EXISTING;
97+
} else {
98+
return PrimaryKeyOptions.NO_PRIMARY_KEY;
99+
}
100+
}
101+
74102
export async function excelToPostgresDb(connectionInfo: Connection, filePath: string, options?: Options): Promise<void> {
103+
104+
if (options?.generatePrimaryKey && options?.useExistingPrimaryKeys) {
105+
return console.error('Cannot generate primary keys column and also use existing primary keys');
106+
}
107+
75108
const sheets = readExcel(filePath);
76109

77110
let insertQuery = '';
78111
let tableQuery = '';
79-
112+
80113
sheets.forEach(async (sheet) => {
81-
tableQuery = tableQuery.concat(createTable(sheet.title, sheet.data[0], options?.useExistingPrimaryKeys));
114+
tableQuery = tableQuery.concat(createTable(sheet.title, sheet.data[0], handlePrimaryKey(options)));
82115
insertQuery = insertQuery.concat(insert(sheet.title, sheet.data));
83116
});
84117

src/tests/sql.test.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
import { createDatabase, createTable, insert } from '../sql';
1+
import { createDatabase, createTable, handlePrimaryKey, insert, PrimaryKeyOptions } from '../sql';
22
import { etlProcesses, sqlInfo, sqlResults } from './test-data';
33

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

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

13-
test('create table multiple primary key', () => {
14-
expect(createTable(sqlInfo.tableName, etlProcesses.sheet_multiple_pk, true)).toEqual(sqlResults.createTableMultiplePrimaryKeys);
13+
test('create table one existing primary key', () => {
14+
expect(createTable(sqlInfo.tableName, etlProcesses.sheet_one_pk, PrimaryKeyOptions.USE_EXISTING)).toEqual(sqlResults.createTableOnePrimaryKey);
15+
});
16+
17+
test('create table multiple existing primary keys', () => {
18+
expect(createTable(sqlInfo.tableName, etlProcesses.sheet_multiple_pk, PrimaryKeyOptions.USE_EXISTING)).toEqual(sqlResults.createTableMultiplePrimaryKeys);
1519
});
1620

1721
test('insert', () => {
1822
expect(insert(sqlInfo.tableName, [etlProcesses.sheet_one_pk])).toEqual(sqlResults.insert);
1923
});
24+
25+
test('handle primary key options', () => {
26+
expect(handlePrimaryKey({ generatePrimaryKey: true })).toEqual(PrimaryKeyOptions.GENERATE);
27+
expect(handlePrimaryKey({ useExistingPrimaryKeys: true })).toEqual(PrimaryKeyOptions.USE_EXISTING);
28+
expect(handlePrimaryKey({ })).toEqual(PrimaryKeyOptions.NO_PRIMARY_KEY);
29+
});
2030
});

src/tests/test-data.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,13 @@ export const etlProcesses = {
9999
`${columns_multiple_pk[1].name} ${columns_multiple_pk[1].type}`,
100100
`${columns_multiple_pk[2].name} ${columns_multiple_pk[2].type}`,
101101
`${SQLKeyword.PRIMARY_KEY} (${columns_multiple_pk[0].name},${columns_multiple_pk[1].name})`
102-
]
102+
],
103+
formattedColumnsGeneratedPrimaryKey: [
104+
`${columns_one_pk[0].name} ${columns_one_pk[0].type}`,
105+
`${columns_one_pk[1].name} ${columns_one_pk[1].type}`,
106+
`${columns_one_pk[2].name} ${columns_one_pk[2].type}`,
107+
`id ${SQLType.INT} ${SQLKeyword.PRIMARY_KEY} ${SQLKeyword.NOT_NULL} ${SQLKeyword.SERIAL}`
108+
],
103109
};
104110

105111
export const sqlInfo = {
@@ -109,6 +115,9 @@ export const sqlInfo = {
109115

110116
export const sqlResults = {
111117
createDatabase: `CREATE DATABASE ${sqlInfo.database};`,
118+
createTableGeneratedPrimaryKey: `CREATE TABLE ${sqlInfo.tableName} (
119+
${etlProcesses.formattedColumnsGeneratedPrimaryKey}
120+
);`,
112121
createTableOnePrimaryKey: `CREATE TABLE ${sqlInfo.tableName} (
113122
${etlProcesses.formattedColumnsOnePrimaryKey}
114123
);`,

0 commit comments

Comments
 (0)