Skip to content

Commit

Permalink
feat: add maybeOne method
Browse files Browse the repository at this point in the history
  • Loading branch information
gajus committed Apr 14, 2017
1 parent 110eed7 commit b1618d8
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 6 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ A PostgreSQL client with strict types and assertions.
* [`any`](#any)
* [`insert`](#insert)
* [`many`](#many)
* [`maybeOne`](#maybeone)
* [`one`](#one)
* [Error handling](#error-handling)
* [Handling `NotFoundError`](#handling-notfounderror)
Expand Down Expand Up @@ -171,6 +172,22 @@ const rows = await connection.many('SELECT foo');

```

### `maybeOne`

Selects the first row from the result.

* Returns `null` if row is not found.
* Throws `DataIntegrityError` if query returns multiple rows.

Example:

```js
const row = await connection.one('SELECT foo');

// row.foo is the result of the `foo` column value of the first row.

```

### `one`

Selects the first row from the result.
Expand Down
24 changes: 24 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
DatabaseSingleConnectionType,
InternalQueryAnyType,
InternalQueryManyType,
InternalQueryMaybeOneType,
InternalQueryOneType,
InternalQueryType
} from './types';
Expand Down Expand Up @@ -113,6 +114,27 @@ export const one: InternalQueryOneType = async (connection, sql, values) => {
return rows[0];
};

/**
* Makes a query and expects exactly one result.
*
* @throws DataIntegrityError If query returns multiple rows.
*/
export const maybeOne: InternalQueryMaybeOneType = async (connection, sql, values) => {
const {
rows
} = await query(connection, sql, values);

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

if (rows.length > 1) {
throw new DataIntegrityError();
}

return rows[0];
};

/**
* Makes a query and expects at least 1 result.
*
Expand Down Expand Up @@ -150,6 +172,7 @@ const createConnection = async (configuration: DatabaseConfigurationType): Promi
return pool.end();
},
many: many.bind(null, connection),
maybeOne: maybeOne.bind(null, connection),
one: one.bind(null, connection),
query: query.bind(null, connection)
};
Expand All @@ -161,6 +184,7 @@ const createPool = (configuration: DatabaseConfigurationType): DatabasePoolConne
return {
any: any.bind(null, pool),
many: many.bind(null, pool),
maybeOne: maybeOne.bind(null, pool),
one: one.bind(null, pool),
query: query.bind(null, pool)
};
Expand Down
15 changes: 9 additions & 6 deletions src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,16 @@ export type DatabaseQueryValuesType =
AnonymouseValuePlaceholderValuesType |
NamedValuePlaceholderValuesType;

export type InternalQueryAnyType = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<QueryResultRowType>>;
export type InternalQueryManyType = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<QueryResultRowType>>;
export type InternalQueryMaybeOneType = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<QueryResultRowType | null>;
export type InternalQueryOneType = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<QueryResultRowType>;

// eslint-disable-next-line flowtype/no-weak-types
export type InternalQueryType = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<any>;
export type InternalQueryOneType = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<QueryResultRowType>;
export type InternalQueryManyType = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<QueryResultRowType>>;
export type InternalQueryAnyType = (connection: InternalDatabaseConnectionType, sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<QueryResultRowType>>;

export type QueryType<T: QueryResultRowType> = (sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
export type QueryOneType<T: QueryResultRowType> = (sql: string, values?: DatabaseQueryValuesType) => Promise<T>;
export type QueryManyType<T: QueryResultRowType> = (sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
export type QueryAnyType<T: QueryResultRowType> = (sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
export type QueryManyType<T: QueryResultRowType> = (sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
export type QueryMaybeOneType<T: QueryResultRowType | null> = (sql: string, values?: DatabaseQueryValuesType) => Promise<T>;
export type QueryOneType<T: QueryResultRowType> = (sql: string, values?: DatabaseQueryValuesType) => Promise<T>;
export type QueryType<T: QueryResultRowType> = (sql: string, values?: DatabaseQueryValuesType) => Promise<$ReadOnlyArray<T>>;
63 changes: 63 additions & 0 deletions test/mightyql/maybeOne.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// @flow

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

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

test('returns the first row', async (t) => {
const stub = sinon.stub().returns({
rows: [
{
foo: 1
}
]
});

const connection: any = {
query: stub
};

const result = await maybeOne(connection, 'SELECT foo FROM bar');

t.deepEqual(result, {
foo: 1
});
});

test('returns null if no results', async (t) => {
const stub = sinon.stub().returns({
rows: []
});

const connection: any = {
query: stub
};

const result = await maybeOne(connection, 'SELECT foo FROM bar');

t.true(result === null);
});

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

const connection: any = {
query: stub
};

await t.throws(maybeOne(connection, 'SELECT foo FROM bar'), DataIntegrityError);
});

0 comments on commit b1618d8

Please sign in to comment.