Skip to content

ColumnValue introduction #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions __tests__/transaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ test.only('Simple Transaction', async () => {
expect(results).toBe('Transaction Committed');

const endInfo = await rds.query(`SELECT COUNT(*) AS cn FROM ${TABLE}`);
const startCount = startInfo.data[0].cn.number;
const endCount = endInfo.data[0].cn.number;
const startCount = startInfo.data[0].cn.number || 0;
const endCount = endInfo.data[0].cn.number || 0;

expect(results).toBe('Transaction Committed');
expect(startCount).toBe(endCount! - 3);
expect(startCount).toBe(endCount - 3);
});

test('Rollback Transaction', async () => {
Expand Down
41 changes: 41 additions & 0 deletions src/ColumnValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { RDSDataService } from "aws-sdk";

class ColumnValue {
public field: RDSDataService.Field;

constructor(field: RDSDataService.Field) {
this.field = field;
}

get isNull(): boolean {
return !!this.field.isNull;
}

get date(): Date | null {
if (this.isNull) return null;
return new Date(this.field.stringValue!);
}

get string(): string | null {
if (this.isNull) return null;
return this.field.stringValue || null;
}

get number(): number | null {
if (this.isNull) return null;
return this.field.longValue || null;
}

get buffer(): Buffer | null {
if (this.isNull) return null;
return Buffer.from((this.field.blobValue || '').toString());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should do we call .toString() on a value that I'd presume to be a string?

Copy link
Collaborator

@MarkHerhold MarkHerhold Sep 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think this logic is wrong for binary type.

The AWS docs say:

blobValue - A value of BLOB data type. Type: Base64-encoded binary data object

So I think we would need to do Buffer.from(this.field.blobValue, 'base64'). I don't see a test that explicitly tests this logic and I haven't verified my assumption with the real RDS Data API.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disregard my prior comment - that's the API documentation, not the JS SDK docs.

It does look like blobValue is already a Buffer type so this value manipulation would be unnecessary, both in this PR and on master.

image

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MarkHerhold - looks like the blobValue can return as Buffer|Uint8Array|Blob|string possible answer I am committing now:

return Buffer.isBuffer(this.field.blobValue) ? this.field.blobValue : Buffer.from(this.field.blobValue as Uint8Array);

}

get boolean(): boolean | null {
if (this.isNull) return null;
return this.field.booleanValue || null;
}

}

export default ColumnValue;
88 changes: 18 additions & 70 deletions src/RDSData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { RDSDataService } from 'aws-sdk';
import { SqlParametersList } from 'aws-sdk/clients/rdsdataservice';
import ColumnValue from './ColumnValue';

export interface RDSDataOptions {
secretArn: string;
Expand All @@ -9,8 +10,7 @@ export interface RDSDataOptions {
rdsConfig?: RDSDataService.Types.ClientConfiguration;
}

// COLUMNS & PARAMETERS -------------------------
export interface RDSDataColumn {
export interface DataColumn {
name: string;
tableName: string;
type: string;
Expand All @@ -24,29 +24,19 @@ export interface RDSDataType {
}
export type RDSDataTypes = 'stringValue' | 'booleanValue' | 'longValue' | 'isNull' | 'blobValue' | undefined;

export type RDSDataParameterValue = string | Buffer | boolean | number | null | undefined;
export type ParameterValue = string | Buffer | boolean | number | null | undefined;
export interface RDSDataParameters {
[key: string]: RDSDataParameterValue;
[key: string]: ParameterValue;
}
export type RDSDataRow = { [key: string]: RDSDataResponseValue };
export type Row = { [key: string]: ColumnValue };

// RESPONSE TYPES -------------------------------
export interface RDSDataResponseValue {
isNull: boolean;
string?: string;
date?: Date;
boolean?: boolean;
buffer?: Buffer;
number?: number;
export interface ResponseData {
[key: string]: ColumnValue;
}

export interface RDSDataResponseData {
[key: string]: RDSDataResponseValue;
}

export interface RDSDataResponse {
columns: RDSDataColumn[];
data: RDSDataResponseData[];
export interface Response {
columns: DataColumn[];
data: ResponseData[];
numberOfRecordsUpdated: number;
insertId: number | undefined;
}
Expand Down Expand Up @@ -80,7 +70,7 @@ export class RDSData {
return this.rds;
}

public async query(sql: string, params?: RDSDataParameters, transactionId?: string): Promise<RDSDataResponse> {
public async query(sql: string, params?: RDSDataParameters, transactionId?: string): Promise<Response> {
const parameters = RDSData.formatParameters(params);
return new Promise((resolve, reject) => {
let queryParameters: RDSDataService.Types.ExecuteStatementRequest = {
Expand Down Expand Up @@ -113,7 +103,7 @@ export class RDSData {
return [];
}

const getType = (val: RDSDataParameterValue): RDSDataTypes => {
const getType = (val: ParameterValue): RDSDataTypes => {
const t = typeof val;
if (t === 'string') {
return 'stringValue';
Expand All @@ -133,7 +123,7 @@ export class RDSData {
return undefined;
};

const formatType = (name: string, value: RDSDataParameterValue, type: RDSDataTypes): RDSDataType => {
const formatType = (name: string, value: ParameterValue, type: RDSDataTypes): RDSDataType => {
if (!type) {
throw new Error(`Invalid Type for name: ${name} value: ${value} type: ${type} typeof: ${typeof value}`);
}
Expand Down Expand Up @@ -163,11 +153,11 @@ export class RDSData {
return parameters;
}

private static resultFormat(response: RDSDataService.Types.ExecuteStatementResponse): RDSDataResponse {
private static resultFormat(response: RDSDataService.Types.ExecuteStatementResponse): Response {
const insertId =
response.generatedFields && response.generatedFields.length > 0 ? response.generatedFields[0].longValue : 0;
const columns: RDSDataColumn[] = [];
const data: { [key: string]: RDSDataResponseValue }[] = [];
const columns: DataColumn[] = [];
const data: { [key: string]: ColumnValue }[] = [];
const numberOfRecordsUpdated = response.numberOfRecordsUpdated ?? 0;

if (response && response.columnMetadata && response.records) {
Expand All @@ -180,51 +170,9 @@ export class RDSData {
});

response.records.forEach((record) => {
const row: RDSDataRow = {};
const row: Row = {};
for (let c = 0; c < record.length; c += 1) {
/* tslint:disable:no-string-literal */
const isNull = record[c].isNull ?? false;
const v: RDSDataResponseValue = { isNull };
switch (columns[c].type) {
case 'BINARY':
v.buffer = isNull ? undefined : Buffer.from((record[c].blobValue || '').toString());
v.string = isNull ? undefined : v.buffer?.toString('base64');
break;
case 'BOOL':
case 'BIT':
v.boolean = isNull ? undefined : record[c].booleanValue;
v.number = v.boolean ? 1 : 0;
break;
case 'TIMESTAMP':
case 'DATETIME':
case 'DATE':
v.date = isNull ? undefined : new Date(record[c].stringValue ?? '');
v.string = isNull ? undefined : record[c].stringValue;
v.number = v.date ? v.date.getTime() : 0;
break;
case 'INTEGER':
case 'INTEGER UNSIGNED':
case 'INT':
case 'INT4':
case 'INT8':
case 'INT UNSIGNED':
case 'BIGINT':
case 'BIGINT UNSIGNED':
case 'SERIAL':
v.number = isNull ? undefined : record[c].longValue;
break;
case 'UUID':
case 'TEXT':
case 'CHAR':
case 'BPCHAR':
case 'VARCHAR':
v.string = isNull ? undefined : record[c].stringValue;
break;
default:
throw new Error(`Missing type: ${columns[c].type}`);
}
/* tslint:enable:no-string-literal */
row[columns[c].name] = v;
row[columns[c].name] = new ColumnValue(record[c]);
}
data.push(row);
});
Expand Down