Skip to content

Commit

Permalink
feat: add anyFirst and manyFirst
Browse files Browse the repository at this point in the history
  • Loading branch information
gajus committed Oct 21, 2017
1 parent 80d2c6e commit 161782e
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 0 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ A PostgreSQL client with strict types and assertions.
* [Guarding against accidental unescaped input](#guarding-against-accidental-unescaped-input)
* [Query methods](#query-methods)
* [`any`](#any)
* [`anyFirst`](#anyfirst)
* [`insert`](#insert)
* [`many`](#many)
* [`manyFirst`](#manyfirst)
* [`maybeOne`](#maybeone)
* [`maybeOneFirst`](#maybeonefirst)
* [`one`](#one)
Expand Down Expand Up @@ -225,6 +227,19 @@ const rows = await connection.any('SELECT foo');

```

### `anyFirst`

Returns value of the first column of every row in the result set.

* Throws `DataIntegrityError` if query returns multiple rows.

Example:

```js
const fooValues = await connection.any('SELECT foo');

```

### `insert`

Designed to use when inserting 1 row.
Expand Down Expand Up @@ -254,6 +269,20 @@ const rows = await connection.many('SELECT foo');

```

### `manyFirst`

Returns value of the first column of every row in the result set.

* Throws `NotFoundError` if query returns no rows.
* Throws `DataIntegrityError` if query returns multiple rows.

Example:

```js
const fooValues = await connection.many('SELECT foo');

```

### `maybeOne`

Selects the first row from the result.
Expand Down
56 changes: 56 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import type {
DatabaseConfigurationType,
DatabasePoolType,
DatabaseSingleConnectionType,
InternalQueryAnyFirstType,
InternalQueryAnyType,
InternalQueryManyFirstType,
InternalQueryManyType,
InternalQueryMaybeOneFirstType,
InternalQueryMaybeOneType,
Expand Down Expand Up @@ -213,6 +215,30 @@ export const many: InternalQueryManyType = async (connection, clientConfiguratio
return rows;
};

export const manyFirst: InternalQueryManyFirstType = async (connection, clientConfigurationType, rawSql, values) => {
const rows = await many(connection, clientConfigurationType, rawSql, values);

if (rows.length === 0) {
throw new DataIntegrityError();
}

const keys = Object.keys(rows[0]);

if (keys.length !== 1) {
throw new DataIntegrityError();
}

const firstColumnName = keys[0];

if (typeof firstColumnName !== 'string') {
throw new DataIntegrityError();
}

return rows.map((row) => {
return row[firstColumnName];
});
};

/**
* Makes a query and expects any number of results.
*/
Expand All @@ -224,6 +250,30 @@ export const any: InternalQueryAnyType = async (connection, clientConfiguration,
return rows;
};

export const anyFirst: InternalQueryAnyFirstType = async (connection, clientConfigurationType, rawSql, values) => {
const rows = await any(connection, clientConfigurationType, rawSql, values);

if (rows.length === 0) {
return [];
}

const keys = Object.keys(rows[0]);

if (keys.length !== 1) {
throw new DataIntegrityError();
}

const firstColumnName = keys[0];

if (typeof firstColumnName !== 'string') {
throw new DataIntegrityError();
}

return rows.map((row) => {
return row[firstColumnName];
});
};

export const transaction: InternalTransactionType = async (connection, handler) => {
await connection.query('START TRANSACTION');

Expand Down Expand Up @@ -259,6 +309,7 @@ const createConnection = async (

const bindConnection = {
any: mapTaggedTemplateLiteralInvocation(any.bind(null, connection, clientConfiguration)),
anyFirst: mapTaggedTemplateLiteralInvocation(anyFirst.bind(null, connection, clientConfiguration)),
end: async () => {
if (ended) {
return ended;
Expand All @@ -271,6 +322,7 @@ const createConnection = async (
return ended;
},
many: mapTaggedTemplateLiteralInvocation(many.bind(null, connection, clientConfiguration)),
manyFirst: mapTaggedTemplateLiteralInvocation(manyFirst.bind(null, connection, clientConfiguration)),
maybeOne: mapTaggedTemplateLiteralInvocation(maybeOne.bind(null, connection, clientConfiguration)),
maybeOneFirst: mapTaggedTemplateLiteralInvocation(maybeOneFirst.bind(null, connection, clientConfiguration)),
one: mapTaggedTemplateLiteralInvocation(one.bind(null, connection, clientConfiguration)),
Expand All @@ -295,7 +347,9 @@ const createPool = (

const bindConnection = {
any: mapTaggedTemplateLiteralInvocation(any.bind(null, connection, clientConfiguration)),
anyFirst: mapTaggedTemplateLiteralInvocation(anyFirst.bind(null, connection, clientConfiguration)),
many: mapTaggedTemplateLiteralInvocation(many.bind(null, connection, clientConfiguration)),
manyFirst: mapTaggedTemplateLiteralInvocation(manyFirst.bind(null, connection, clientConfiguration)),
maybeOne: mapTaggedTemplateLiteralInvocation(maybeOne.bind(null, connection, clientConfiguration)),
maybeOneFirst: mapTaggedTemplateLiteralInvocation(maybeOneFirst.bind(null, connection, clientConfiguration)),
one: mapTaggedTemplateLiteralInvocation(one.bind(null, connection, clientConfiguration)),
Expand All @@ -312,8 +366,10 @@ const createPool = (

return {
any: mapTaggedTemplateLiteralInvocation(any.bind(null, pool, clientConfiguration)),
anyFirst: mapTaggedTemplateLiteralInvocation(anyFirst.bind(null, pool, clientConfiguration)),
connect,
many: mapTaggedTemplateLiteralInvocation(many.bind(null, pool, clientConfiguration)),
manyFirst: mapTaggedTemplateLiteralInvocation(manyFirst.bind(null, pool, clientConfiguration)),
maybeOne: mapTaggedTemplateLiteralInvocation(maybeOne.bind(null, pool, clientConfiguration)),
maybeOneFirst: mapTaggedTemplateLiteralInvocation(maybeOneFirst.bind(null, pool, clientConfiguration)),
one: mapTaggedTemplateLiteralInvocation(one.bind(null, pool, clientConfiguration)),
Expand Down
18 changes: 18 additions & 0 deletions src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ export type DatabaseConfigurationType =

export type DatabaseConnectionType = {
+any: QueryAnyType<*>,
+anyFirst: QueryAnyFirstType<*>,
+many: QueryManyType<*>,
+manyFirst: QueryManyFirstType<*>,
+maybeOne: QueryMaybeOneType<*>,
+maybeOneFirst: QueryMaybeOneFirstType<*>,
+one: QueryOneType<*>,
Expand Down Expand Up @@ -111,13 +113,27 @@ export type InternalQueryAnyType = (
values?: DatabaseQueryValuesType
) => Promise<$ReadOnlyArray<QueryResultRowType>>;

export type InternalQueryAnyFirstType = (
connection: InternalDatabaseConnectionType,
clientConfiguration: ClientConfigurationType,
sql: string,
values?: DatabaseQueryValuesType
) => Promise<$ReadOnlyArray<QueryResultRowColumnType>>;

export type InternalQueryManyType = (
connection: InternalDatabaseConnectionType,
clientConfiguration: ClientConfigurationType,
sql: string,
values?: DatabaseQueryValuesType
) => Promise<$ReadOnlyArray<QueryResultRowType>>;

export type InternalQueryManyFirstType = (
connection: InternalDatabaseConnectionType,
clientConfiguration: ClientConfigurationType,
sql: string,
values?: DatabaseQueryValuesType
) => Promise<$ReadOnlyArray<QueryResultRowColumnType>>;

export type InternalQueryMaybeOneFirstType = (
connection: InternalDatabaseConnectionType,
clientConfiguration: ClientConfigurationType,
Expand Down Expand Up @@ -152,7 +168,9 @@ export type InternalTransactionType = (connection: InternalDatabaseConnectionTyp

export type InternalQueryType<T: QueryResultRowType> = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<QueryResultType<T>>;

export type QueryAnyFirstType<T: QueryResultRowColumnType> = (sql: string | TaggledTemplateLiteralInvocationType, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
export type QueryAnyType<T: QueryResultRowType> = (sql: string | TaggledTemplateLiteralInvocationType, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
export type QueryManyFirstType<T: QueryResultRowColumnType> = (sql: string | TaggledTemplateLiteralInvocationType, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
export type QueryManyType<T: QueryResultRowType> = (sql: string | TaggledTemplateLiteralInvocationType, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
export type QueryMaybeOneFirstType<T: QueryResultRowColumnType> = (sql: string | TaggledTemplateLiteralInvocationType, values?: DatabaseQueryValuesType) => Promise<T>;
export type QueryMaybeOneType<T: QueryResultRowType | null> = (sql: string | TaggledTemplateLiteralInvocationType, values?: DatabaseQueryValuesType) => Promise<T>;
Expand Down
51 changes: 51 additions & 0 deletions test/mightyql/anyFirst.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// @flow

/* eslint-disable flowtype/no-weak-types */

import test from 'ava';
import sinon from 'sinon';
import {
anyFirst,
DataIntegrityError
} from '../../src';

test('returns values of the query result rows', async (t) => {
const stub = sinon.stub().returns({
rows: [
{
foo: 1
},
{
foo: 2
}
]
});

const connection: any = {
query: stub
};

const result = await anyFirst(connection, {}, '');

t.deepEqual(result, [
1,
2
]);
});

test('throws an error if more than one column is returned', async (t) => {
const stub = sinon.stub().returns({
rows: [
{
bar: 1,
foo: 1
}
]
});

const connection: any = {
query: stub
};

await t.throws(anyFirst(connection, {}, ''), DataIntegrityError);
});
51 changes: 51 additions & 0 deletions test/mightyql/manyFirst.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// @flow

/* eslint-disable flowtype/no-weak-types */

import test from 'ava';
import sinon from 'sinon';
import {
DataIntegrityError,
manyFirst
} from '../../src';

test('returns values of the query result rows', async (t) => {
const stub = sinon.stub().returns({
rows: [
{
foo: 1
},
{
foo: 2
}
]
});

const connection: any = {
query: stub
};

const result = await manyFirst(connection, {}, '');

t.deepEqual(result, [
1,
2
]);
});

test('throws an error if more than one column is returned', async (t) => {
const stub = sinon.stub().returns({
rows: [
{
bar: 1,
foo: 1
}
]
});

const connection: any = {
query: stub
};

await t.throws(manyFirst(connection, {}, ''), DataIntegrityError);
});

0 comments on commit 161782e

Please sign in to comment.