-
Notifications
You must be signed in to change notification settings - Fork 1
/
mutate-model.ts
112 lines (94 loc) · 3.5 KB
/
mutate-model.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { assert } from '../error/';
import { ColumnStore, TableStore, RelationshipStore, PropertyMapStore, EntityType } from '../metadata/';
import { ParameterList, ParameterType, Query, Escaper, From,
ExecutableQuery } from './';
/**
* A [[Query]] that represents a mutation (update or delete) on an entity by
* ID.
*/
export abstract class MutateModel<Q extends Query, T> extends Query {
/**
* A [[From]] instance that is used to create a [[Query]] of type Q.
*/
protected from: From;
/**
* A [[Query]] instance (e.g. [[Update]] or [[Delete]] that is used in
* toString, buildQuery, and execute.
*/
protected query: Q;
/**
* Initialize the query using an Entity type and an Entity instance (a
* model).
* @param colStore - Used for accessing columns in tables.
* @param tblStore - Used for accessing tables in the database.
* @param relStore - Used for accessing relationships between tables.
* @param propStore - Used for pulling table property maps (used in
* conjunction with the relStore to get remote columns).
* @param escaper - An [[Escaper]] matching the database type (e.g.
* [[MySQLEscaper]] or [[MSSQLEscaper]]). Used when escaping column names in
* compiled conditions.
* @param Entity - The type of model to mutate, which is the constructor of a
* [[Table]]-decorated class.
* @param model - An Entity instance to mutate, which must have the primary
* key set.
*/
constructor(
protected colStore: ColumnStore,
protected tblStore: TableStore,
protected relStore: RelationshipStore,
protected propStore: PropertyMapStore,
protected escaper: Escaper,
protected Entity: EntityType<T>,
protected model: T) {
super();
this.from = this.createFrom();
this.query = this.produceQuery(this.from);
}
/**
* Create the [[From]] instance that will be used when mutating the model.
*/
private createFrom(): From {
// Update is From the Entity table.
const from = new From(this.colStore, this.tblStore, this.relStore,
this.propStore, this.escaper, this.Entity);
const fromTblAlias = from.getBaseTableMeta().alias;
// Update is by primary key: add the where clause.
const pkColMetas = this.colStore.getPrimaryKey(this.Entity);
const cond: any = {$and: []};
const paramList = new ParameterList();
pkColMetas
.forEach(pkColMeta => {
const pkVal = (this.model as ParameterType)[pkColMeta.mapTo];
const fqProp = `${fromTblAlias}.${pkColMeta.mapTo}`;
const paramName = paramList.createParameterName(fqProp);
assert(pkVal !== undefined && pkVal !== null,
`The primary key is required when mutating a model, but "${pkColMeta.mapTo}" is missing.`);
paramList.addParameter(paramName, pkVal);
cond.$and.push({$eq: {[fqProp]: `:${paramName}`}});
});
from
.where(cond, paramList.getParams());
return from;
}
/**
* Produce a [[Query]] instance that's appropriate for the database (e.g. a
* [[MySQLUpdate]]).
* @param from - A [[From]] instance, passed to the [[Query]] constructor.
*/
protected abstract produceQuery(from: From): Q;
/**
* Get the SQL that represents the query.
* @return The SQL representing the mutation.
*/
toString(): string {
return this.query.toString();
}
/**
* Build the query.
* @return The string-representation of the query to execute along with query
* parameters.
*/
buildQuery(): ExecutableQuery {
return this.query.buildQuery();
}
}