Skip to content

Commit

Permalink
adding update method to collection class
Browse files Browse the repository at this point in the history
  • Loading branch information
iotshaman committed May 15, 2021
1 parent da0d874 commit 961247c
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 287 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export declare class Collection<T> {
findOne: (query: EntityQuery) => Promise<T>;
insert: (query: EntityQuery) => Promise<void>;
insertOne: (model: T) => Promise<number>;
update: (model: T, query: EntityQuery) => Promise<void>;
updateOne: (model: T, query: EntityQuery) => Promise<void>;
delete: (query: EntityQuery) => Promise<void>;
deleteOne: (query: EntityQuery) => Promise<void>;
Expand Down Expand Up @@ -234,6 +235,17 @@ Insert one object (T) into a table.
insertOne: (model: T) => Promise<number>;
```

#### update
Takes an object (T) and an "EntityQuery" object and updates the corresponding database values. Below is a list of all "EntityQuery" properties that are available (all properties are required):

```ts
update: (model: T, query: EntityQuery) => Promise<void>;
```

* **columns**: A list of columns to update; only these column values will be updated.
* **conditions** - List of "WHERE" clauses. Please use '?' to represent query parameters, then pass those parameters in the 'args' property; by doing so, mysql will sanitize the inputs, helping prevent SQL injection.
* **args** - A value that represents a unique object, and should match a value in the column specified in the "identity" parameter.

#### updateOne
Takes an object (T) and an "EntityQuery" object and updates the corresponding database values. Below is a list of all "EntityQuery" properties that are available (all properties are required):

Expand Down
558 changes: 292 additions & 266 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mysql-shaman",
"version": "0.0.4",
"version": "0.0.5",
"description": "Access MySql databases using a simple, familiar ORM syntax.",
"main": "dist/index.js",
"scripts": {
Expand Down Expand Up @@ -29,21 +29,21 @@
},
"homepage": "https://github.com/iotshaman/mysql-shaman#readme",
"devDependencies": {
"@types/chai": "~4.2.14",
"@types/mocha": "^8.0.3",
"@types/mysql": "2.15.15",
"@types/chai": "~4.2.18",
"@types/mocha": "^8.2.2",
"@types/mysql": "2.15.18",
"@types/node": "^14.14.2",
"@types/sinon": "9.0.8",
"chai": "^4.2.0",
"@types/sinon": "10.0.0",
"chai": "^4.3.4",
"coveralls": "^3.1.0",
"mocha": "^8.2.0",
"mocha": "^8.4.0",
"nyc": "^15.1.0",
"sinon": "^9.2.0",
"ts-node": "^9.0.0",
"sinon": "^10.0.0",
"ts-node": "^9.1.1",
"typescript": "^4.0.3"
},
"dependencies": {
"fast-glob": "^3.2.4",
"fast-glob": "^3.2.5",
"mysql": "^2.18.1"
},
"nyc": {
Expand Down
31 changes: 22 additions & 9 deletions src/collection.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PoolConnection, OkPacket } from 'mysql';
import { EntityQuery } from './entity-query';
import { RunMySqlQuery, GetMySqlColumns, GetMySqlConditions } from './mysql.functions';
import { RunMySqlQuery, GetMySqlColumns, GetMySqlConditions, GetMySqlUpdateColumns } from './mysql.functions';

export class Collection<T> {

Expand All @@ -17,14 +17,14 @@ export class Collection<T> {
let columns = GetMySqlColumns(query);
let qString = `SELECT ${columns} FROM ${this.name}`;
let conditions = GetMySqlConditions(query);
if (!conditions) return this.execute(`${qString};`, query.args);
if (!conditions) return this.execute(`${qString};`, query.args, query.debug);
return this.execute(`${qString} WHERE ${conditions};`, query.args);
}

findOne = (query: EntityQuery): Promise<T> => {
let columns = GetMySqlColumns(query);
let qString = `SELECT ${columns} FROM ${this.name} WHERE ${query.identity} = ? LIMIT 1;`;
let req = this.execute<T[]>(qString, query.args);
let req = this.execute<T[]>(qString, query.args, query.debug);
return req.then(rslt => rslt.length == 0 ? null : rslt[0]);
}

Expand All @@ -36,12 +36,21 @@ export class Collection<T> {
});
let columns = GetMySqlColumns(query);
let qString = `INSERT INTO ${this.name} (${columns}) VALUES ?`;
return this.execute(`${qString};`, [args]);
return this.execute(`${qString};`, [args], query.debug);
}

insertOne = (model: T): Promise<number> => {
insertOne = (model: T, debug: boolean = false): Promise<number> => {
let qString = `INSERT INTO ${this.name} SET ?;`;
return this.execute<OkPacket>(qString, model).then(ok => ok.insertId);
return this.execute<OkPacket>(qString, model, debug).then(ok => ok.insertId);
}

update = (model: T, query: EntityQuery): Promise<void> => {
let columns = GetMySqlUpdateColumns(query);
let conditions = GetMySqlConditions(query);
let qString = `UPDATE ${this.name} SET ${columns} WHERE ${conditions}`;
let columnValues = query.columns.map(c => model[c]);
let args = [...columnValues, ...query.args];
return this.execute<void>(qString, args, query.debug).then(_ => (null));
}

updateOne = (model: T, query: EntityQuery): Promise<void> => {
Expand All @@ -56,15 +65,19 @@ export class Collection<T> {
if (!query.conditions) return Promise.reject("Delete all operation not allowed.");
let qString = `DELETE FROM ${this.name}`;
let conditions = GetMySqlConditions(query);
return this.execute(`${qString} WHERE ${conditions};`, query.args).then(_ => (null));
return this.execute(`${qString} WHERE ${conditions};`, query.args, query.debug).then(_ => (null));
}

deleteOne = (query: EntityQuery): Promise<void> => {
let qString = `DELETE FROM ${this.name} WHERE ${query.identity} = ?;`;
return this.execute<void>(qString, query.args).then(_ => (null));
return this.execute<void>(qString, query.args, query.debug).then(_ => (null));
}

private execute<T>(query: string, args: any = null) {
private execute<T>(query: string, args: any = null, debug: boolean = false) {
if (!!debug) {
console.log(`Query string: ${query}`);
console.log(`Query params: ${JSON.stringify(args)}`);
}
return this.connectionFactory()
.then(conn => RunMySqlQuery<T>(conn, query, args));
}
Expand Down
14 changes: 13 additions & 1 deletion src/collections.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ describe('Collections', () => {
getPoolConnectionMock(null, {insertId: 0}).then((connection: any) => {
foo = new Collection();
foo.initialize('foo', () => Promise.resolve(connection));
foo.insertOne({bar: 0}).then(rslt => {
foo.insertOne({bar: 0}, true).then(rslt => {
expect(rslt).to.equal(0);
done();
});
Expand Down Expand Up @@ -127,6 +127,18 @@ describe('Collections', () => {
});
});

it('update should run update query', (done) => {
getPoolConnectionMock().then((connection: any) => {
foo = new Collection();
foo.initialize('foo', () => Promise.resolve(connection));
foo.update({id: 0, bar: 0}, {columns: ['bar'], conditions: ['foo = ?'], args: [0]}).then(_ => {
let query = connection.query.getCall(0).args[0];
expect(query).to.equal('UPDATE foo SET bar = ? WHERE foo = ?');
done();
});
});
});

it('delete should throw error if no conditions provided', (done) => {
getPoolConnectionMock(null, {insertId: 0}).then((connection: any) => {
foo = new Collection();
Expand Down
1 change: 1 addition & 0 deletions src/entity-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export interface EntityQuery {
columns?: string[];
conditions?: string[];
limit?: number;
debug?: boolean;
}
26 changes: 25 additions & 1 deletion src/mysql.functions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
RunMySqlQuery,
CreateCommaDelimitedList,
GetMySqlColumns,
GetMySqlConditions
GetMySqlConditions,
GetMySqlUpdateColumns
} from './mysql.functions';

describe('MySql Functions', () => {
Expand Down Expand Up @@ -68,6 +69,29 @@ describe('MySql Functions', () => {
let result = GetMySqlColumns({columns: ['a', 'b', 'c']});
expect(result).to.equal('a, b, c');
});
//
it('GetMySqlUpdateColumns should throw if query is null', () => {
try {
GetMySqlUpdateColumns(null);
throw new Error("Should have thrown.")
} catch(ex) {
expect(ex.message).to.equal("Columns must be provided when performing update.");
}
});

it('GetMySqlUpdateColumns should throw if no columns provided', () => {
try {
GetMySqlUpdateColumns({});
throw new Error("Should have thrown.")
} catch(ex) {
expect(ex.message).to.equal("Columns must be provided when performing update.");
}
});

it('GetMySqlUpdateColumns should return comma delimited list', () => {
let result = GetMySqlUpdateColumns({columns: ['a', 'b', 'c']});
expect(result).to.equal('a = ?, b = ?, c = ?');
});

it('GetMySqlConditions should return empty string if query is null', () => {
let result = GetMySqlConditions(null);
Expand Down
5 changes: 5 additions & 0 deletions src/mysql.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export function GetMySqlColumns(query: EntityQuery): string {
return CreateCommaDelimitedList(query.columns);
}

export function GetMySqlUpdateColumns(query: EntityQuery): string {
if (!query || !query.columns) throw new Error("Columns must be provided when performing update.");
return CreateCommaDelimitedList(query.columns.map(c => `${c} = ?`));
}

export function GetMySqlConditions(query: EntityQuery) {
if (!query || !query.conditions || query.conditions.length == 0) return '';
return query.conditions.reduce((a, b) => {
Expand Down

0 comments on commit 961247c

Please sign in to comment.