Skip to content

Commit

Permalink
feat(postgres): start implementing update
Browse files Browse the repository at this point in the history
  • Loading branch information
calebmer committed Oct 9, 2016
1 parent db60e03 commit 7318a5e
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 22 deletions.
11 changes: 8 additions & 3 deletions src/interface/collection/CollectionKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,26 @@ interface CollectionKey<TValue, TKey, TKeyType extends Type<TKey>> {

/**
* Reads a single value from the collection using that value’s key.
*
* If nothing was found, return `null`.
*/
// TODO: Test this.
read?: ((context: mixed, key: TKey) => Promise<TValue | undefined>) | null
read?: ((context: mixed, key: TKey) => Promise<TValue | null>) | null

/**
* Updates a value in the collection by using that value’s key. Returned is
* value after the updates have been applied.
*
* If nothing was updated, an error should be thrown.
*/
// TODO: Patch object.
// TODO: Test this.
update?: ((context: mixed, key: TKey) => Promise<TValue>) | null
update?: ((context: mixed, key: TKey, patch: Map<string, mixed>) => Promise<TValue>) | null

/**
* Delete a value from the collection by using the value’s key. Returned is
* the value before it was deleted.
*
* If nothing was deleted an error should be thrown.
*/
// TODO: Test this.
delete?: ((context: mixed, key: TKey) => Promise<TValue>) | null
Expand Down
81 changes: 62 additions & 19 deletions src/postgres/inventory/collection/PGCollectionKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ class PGCollectionKey implements CollectionKey<PGCollectionValue, PGCollectionKe
return this.type.getFields().map(field => value[field.getName()])
}

/**
* Takes a key value and transforms it into a SQL condition which can be used
* in the `where` clause of `select`s, `update`s, and `delete`s.
*
* @private
*/
private _getSQLKeyCondition (key: PGCollectionKeyValue): sql.SQL {
return sql.join(this._pgKeyAttributes.map((pgAttribute, i) =>
sql.query`${sql.identifier(pgAttribute.name)} = ${sql.value(key[i])}`
), ' and ')
}

/**
* Reads a value if a user can select from this class. Batches requests to
* the same client in the background.
Expand Down Expand Up @@ -111,13 +123,8 @@ class PGCollectionKey implements CollectionKey<PGCollectionValue, PGCollectionKe
from ${sql.identifier(this._pgNamespace.name, this._pgClass.name)} as x
-- For all of our key attributes we need to test equality with a
-- key value. Note though, if we don’t find a match no row will
-- be emit for this key. We loop through our keys below to
-- safeguard this from happening.
where
${sql.join(this._pgKeyAttributes.map((pgAttribute, i) =>
sql.query`${sql.identifier(pgAttribute.name)} = ${sql.value(key[i])}`
), ' and ')}
-- key value.
where ${this._getSQLKeyCondition(key)}
-- Combine our selected row with a single null and a limit.
-- This way if our where clause misses, we’ll get the null. If
Expand All @@ -136,6 +143,50 @@ class PGCollectionKey implements CollectionKey<PGCollectionValue, PGCollectionKe
}
)
}

/**
* Updates a value in our Postgres database using a patch object. If no
* value could be updated we should throw an error to let the user know.
*
* This method, unlike many of the other asynchronous actions in Postgres
* collections, is not batched.
*/
public update = (
!this._pgClass.isUpdatable
? null
: async (context: mixed, key: PGCollectionKeyValue, patch: Map<string, mixed>): Promise<PGCollectionValue> => {
// First things first, we need to get our Postgres client from the
// context. This means first verifying our client exists on the
// context.
if (!isPGContext(context)) throw isPGContext.error()
const { client } = context

const query = sql.compile(sql.query`
-- Put our updated rows in a with statement so that we can select
-- our result as JSON rows before returning it.
with updated as (
update ${sql.identifier(this._pgNamespace.name, this._pgClass.name)}
-- Using our patch object we construct the fields we want to set and
-- the values we want to set them to.
set ${Array.from(patch.entries()).map(([fieldName, value]) =>
sql.query`x = ${value === null ? sql.raw('null') : sql.value(value)}`
)}
where ${this._getSQLKeyCondition(key)}
returning *
)
select row_to_json(x) as object from updated as x
`)()

const result = await client.query(query)

if (result.rowCount < 1)
throw new Error('No values were updated.')

return result.rows[0]['object']
}
)
}

export default PGCollectionKey
Expand All @@ -145,10 +196,8 @@ export default PGCollectionKey
* representing objects as a JavaScript object, the key type will represent
* object as an array.
*/
class PGCollectionKeyType extends ObjectType<
PGCollectionKeyValue,
PGCollectionKeyField<mixed, Type<mixed>>,
> {
class PGCollectionKeyType
extends ObjectType<PGCollectionKeyValue, PGCollectionKeyField<mixed, Type<mixed>>> {
constructor (
name: string,
private _pgCatalog: PGCatalog,
Expand All @@ -169,14 +218,8 @@ class PGCollectionKeyType extends ObjectType<
}
}

class PGCollectionKeyField<
TFieldValue,
TFieldType extends Type<TFieldValue>,
> extends ObjectField<
PGCollectionKeyValue,
TFieldValue,
TFieldType,
> {
class PGCollectionKeyField<TFieldValue, TFieldType extends Type<TFieldValue>>
extends ObjectField<PGCollectionKeyValue, TFieldValue, TFieldType> {
constructor (
pgCatalog: PGCatalog,
pgAttribute: PGCatalogAttribute,
Expand Down
2 changes: 2 additions & 0 deletions typings/pg.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export interface QueryConfig {
}

export interface QueryResult {
command: string
rowCount: number
rows: ({ [key: string]: any })[]
}

Expand Down

0 comments on commit 7318a5e

Please sign in to comment.