@villedemontreal/utils-knex 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +236 -0
- package/dist/src/config/configs.d.ts +19 -0
- package/dist/src/config/configs.d.ts.map +1 -0
- package/dist/src/config/configs.js +27 -0
- package/dist/src/config/configs.js.map +1 -0
- package/dist/src/config/constants.d.ts +21 -0
- package/dist/src/config/constants.d.ts.map +1 -0
- package/dist/src/config/constants.js +21 -0
- package/dist/src/config/constants.js.map +1 -0
- package/dist/src/config/init.d.ts +16 -0
- package/dist/src/config/init.d.ts.map +1 -0
- package/dist/src/config/init.js +33 -0
- package/dist/src/config/init.js.map +1 -0
- package/dist/src/databaseContext.d.ts +9 -0
- package/dist/src/databaseContext.d.ts.map +1 -0
- package/dist/src/databaseContext.js +3 -0
- package/dist/src/databaseContext.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/knexUtils.d.ts +134 -0
- package/dist/src/knexUtils.d.ts.map +1 -0
- package/dist/src/knexUtils.js +349 -0
- package/dist/src/knexUtils.js.map +1 -0
- package/dist/src/knexUtils.test.d.ts +2 -0
- package/dist/src/knexUtils.test.d.ts.map +1 -0
- package/dist/src/knexUtils.test.js +897 -0
- package/dist/src/knexUtils.test.js.map +1 -0
- package/dist/src/transactionManager.d.ts +34 -0
- package/dist/src/transactionManager.d.ts.map +1 -0
- package/dist/src/transactionManager.js +75 -0
- package/dist/src/transactionManager.js.map +1 -0
- package/dist/src/transactionManager.test.d.ts +10 -0
- package/dist/src/transactionManager.test.d.ts.map +1 -0
- package/dist/src/transactionManager.test.js +255 -0
- package/dist/src/transactionManager.test.js.map +1 -0
- package/dist/src/utils/logger.d.ts +12 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +53 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/testingConfigurations.d.ts +9 -0
- package/dist/src/utils/testingConfigurations.d.ts.map +1 -0
- package/dist/src/utils/testingConfigurations.js +16 -0
- package/dist/src/utils/testingConfigurations.js.map +1 -0
- package/dist/testing/testClient.d.ts +15 -0
- package/dist/testing/testClient.d.ts.map +1 -0
- package/dist/testing/testClient.js +55 -0
- package/dist/testing/testClient.js.map +1 -0
- package/dist/testing/testRepo.d.ts +8 -0
- package/dist/testing/testRepo.d.ts.map +1 -0
- package/dist/testing/testRepo.js +31 -0
- package/dist/testing/testRepo.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +76 -0
- package/src/config/configs.ts +34 -0
- package/src/config/constants.ts +33 -0
- package/src/config/init.ts +33 -0
- package/src/databaseContext.ts +9 -0
- package/src/index.ts +9 -0
- package/src/knexUtils.test.ts +1526 -0
- package/src/knexUtils.ts +459 -0
- package/src/transactionManager.test.ts +302 -0
- package/src/transactionManager.ts +94 -0
- package/src/utils/logger.ts +60 -0
- package/src/utils/testingConfigurations.ts +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# @villemontreal/core-utils-knex-nodejs-lib
|
|
2
|
+
|
|
3
|
+
Module d'utilitaires pour la librairie Knex.
|
|
4
|
+
|
|
5
|
+
## Availabililty
|
|
6
|
+
|
|
7
|
+
https://bitbucket.org/villemontreal/core-utils-knex-nodejs-lib
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Installer la bibliothèque:
|
|
12
|
+
|
|
13
|
+
```shell
|
|
14
|
+
npm install --save @villemontreal/test-core-utils-knex-nodejs-lib
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Utilisation
|
|
18
|
+
|
|
19
|
+
# Configurations
|
|
20
|
+
|
|
21
|
+
Un code utilisant cette librarie doit premièrement la configurer en appellant la fonction
|
|
22
|
+
"`ìnit(...)`" exportée par le fichier "`src/config/init.ts`".
|
|
23
|
+
|
|
24
|
+
La configuration "`loggerCreator`" est _requise_ par cette librairie. Cela signifie qu'un code utilisant la librairie
|
|
25
|
+
(que ce soit du code d'un projet d'API ou d'une autre librairie) _doit_ setter cette configuration _avant_ que les composants
|
|
26
|
+
de la librairie ne soient utilisés.
|
|
27
|
+
|
|
28
|
+
Par exemple, dans un projet d'API basé sur le générateur
|
|
29
|
+
[generator-mtl-node-api](https://bitbucket.org/villemontreal/generator-mtl-node-api), ceci sera effectué dans le
|
|
30
|
+
fichier "`src/init.ts`", au début de la fonction `initComponents()` :
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { init as initKnexUtilsLib } from '@villemontreal/core-utils-knex-nodejs-lib';
|
|
34
|
+
import { createLogger } from './utils/logger';
|
|
35
|
+
|
|
36
|
+
export async function initComponents() {
|
|
37
|
+
initKnexUtilsLib(createLogger);
|
|
38
|
+
|
|
39
|
+
//...
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Si vous configurez la librairie depuis _une autre librairie_, vous aurez à passer
|
|
44
|
+
le "`Logger Creator`" que vous aurez _vous-même_ reçu comme configurations! :
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { init as initKnexUtilsLib } from '@villemontreal/core-utils-knex-nodejs-lib';
|
|
48
|
+
import { configs } from './configs';
|
|
49
|
+
|
|
50
|
+
export async function initComponents() {
|
|
51
|
+
initKnexUtilsLib(configs.loggerCreator);
|
|
52
|
+
|
|
53
|
+
//...
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Le but étant que toutes les librairies utilisées dans un projet d'API, ainsi que leurs propres librairies
|
|
58
|
+
transitives, puissent logger de la même manière et aient accès aux bons Correlation Ids.
|
|
59
|
+
|
|
60
|
+
Finalement, notez qu'une fonction "`isInited()`" est exportée et permet au code appelant de valider que la librairie a été
|
|
61
|
+
configurée correctement!
|
|
62
|
+
|
|
63
|
+
# Builder le projet
|
|
64
|
+
|
|
65
|
+
**Note**: Sur Linux/Mac assurz-vous que le fichier `run` est exécutable. Autrement, lancez `chmod +x ./run`.
|
|
66
|
+
|
|
67
|
+
Pour lancer le build :
|
|
68
|
+
|
|
69
|
+
- > `run compile` ou `./run compile` (sur Linux/Mac)
|
|
70
|
+
|
|
71
|
+
Pour lancer les tests :
|
|
72
|
+
|
|
73
|
+
- > `run test` ou `./run test` (sur Linux/Mac)
|
|
74
|
+
|
|
75
|
+
# Mode Watch
|
|
76
|
+
|
|
77
|
+
Lors du développement, il est possible de lancer `run watch` (ou `./run watch` sur Linux/mac) dans un terminal
|
|
78
|
+
externe pour démarrer la compilation incrémentale. Il est alors possible de lancer certaines _launch configuration_
|
|
79
|
+
comme `Debug current tests file - fast` dans VsCode et ainsi déboguer le fichier de tests présentement ouvert sans
|
|
80
|
+
avoir à (re)compiler au préalable (la compilation incrémentale s'en sera chargé).
|
|
81
|
+
|
|
82
|
+
Notez que, par défaut, des _notifications desktop_ sont activées pour indiquer visuellement si la compilation
|
|
83
|
+
incrémentale est un succès ou si une erreur a été trouvée. Vous pouvez désactiver ces notifications en utilisant
|
|
84
|
+
`run watch --dn` (`d`isable `n`otifications).
|
|
85
|
+
|
|
86
|
+
# Déboguer le projet
|
|
87
|
+
|
|
88
|
+
Trois "_launch configurations_" sont founies pour déboguer le projet dans VSCode :
|
|
89
|
+
|
|
90
|
+
- "`Debug all tests`", la launch configuration par défaut. Lance les tests en mode debug. Vous pouvez mettre
|
|
91
|
+
des breakpoints et ils seront respectés.
|
|
92
|
+
|
|
93
|
+
- "`Debug a test file`". Lance _un_ fichier de tests en mode debug. Vous pouvez mettre
|
|
94
|
+
des breakpoints et ils seront respectés. Pour changer le fichier de tests à être exécuté, vous devez modifier la ligne appropriée dans le fichier "`.vscode/launch.json`".
|
|
95
|
+
|
|
96
|
+
- "`Debug current tests file`". Lance le fichier de tests _présentement ouvert_ dans VSCode en mode debug. Effectue la compîlation au préalable.
|
|
97
|
+
|
|
98
|
+
- "`Debug current tests file - fast`". Lance le fichier de tests _présentement ouvert_ dans VSCode en mode debug. Aucune compilation
|
|
99
|
+
n'est effectuée au préalable. Cette launch configuration doit être utilisée lorsque la compilation incrémentale roule (voir la section "`Mode Watch`" plus haut)
|
|
100
|
+
|
|
101
|
+
# Gérer les transactions SQL dans son projet/API
|
|
102
|
+
|
|
103
|
+
Dans cette librairie est fourni un composant nommé `KnexTransactionManager` permettant d'exécuter
|
|
104
|
+
plusieures requêtes SQL au sein d'une même transaction. Si une des requêtes échoue,
|
|
105
|
+
toutes les requêtes déjà effectuées seront rollbackées. En d'autres mots, toutes les requêtes doivent
|
|
106
|
+
résulter en un succès ou _aucune_ ne sera commitée.
|
|
107
|
+
|
|
108
|
+
Le pattern utilisé pour arriver à gérer de telles transactions est de passer un `context` (implémentant
|
|
109
|
+
l'interface `IDatabaseContext`) _à chaque méthodes suceptible d'exécuter, directement ou indirectement, une requête SQL_.
|
|
110
|
+
En gros, il s'agit de passer l'object `context`, que nous recevrons nous-mêmes, à toutes les
|
|
111
|
+
méthodes de _services_ ou de _repositories_ que nous appellons.
|
|
112
|
+
|
|
113
|
+
Il est fortement recommandé que ce paramètre `context` soit _toujours_ le premier dans la signature d'une méthode.
|
|
114
|
+
Par exemple, dans un service fictif `UserService`, il pourrait y avoir une méthode `updateUser`
|
|
115
|
+
prennant ce `context` comme premier paramètre:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
public async updateUser(context: IAppContext, userId: number, userToUpdate: IUser) {
|
|
119
|
+
|
|
120
|
+
// On démarre un scope de transaction avec `withTransaction`
|
|
121
|
+
await txManager.withTransaction(context, async (client: knex.Transaction) => {
|
|
122
|
+
|
|
123
|
+
// Appel à une repository, en lui passant le contexte
|
|
124
|
+
const updatedUser = await userRepository.updateUser(context, userId, userToUpdate);
|
|
125
|
+
|
|
126
|
+
// Appel à un autre service, en lui passant le contexte
|
|
127
|
+
await indexationService.updateUserIndex(context, updatedUser);
|
|
128
|
+
|
|
129
|
+
// Fait une requête SQL directement en utilisant
|
|
130
|
+
// le client fourni par `withTransaction`!
|
|
131
|
+
const res = await client(`statistics`).insert({
|
|
132
|
+
something: 'blablabla'
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Dans cet exemple on voit que:
|
|
139
|
+
|
|
140
|
+
- la méthode `updateUser` du service reçoit un `context` comme premier paramètre. C'est ce
|
|
141
|
+
`context` qu'elle devra elle-même passer aux autres méthodes qu'elles appellera. Vous
|
|
142
|
+
avez peut-être remarqué que le type de ce `context` est ici `IAppContext` et non `IDatabaseContext`!
|
|
143
|
+
Il est en effet fréquent qu'une application crée son propre type de context
|
|
144
|
+
_implémentant `IDatabaseContext`_. Ceci lui permet de passer des informations supplémentaires,
|
|
145
|
+
de méthode en méthode! Par exemple, ce context spécialisé pourrait comprendre le `JWT` reçu lors de
|
|
146
|
+
la requête HTTP, les paramètres de cette requête HTTP, etc. Par exemple:
|
|
147
|
+
```typescript
|
|
148
|
+
export interface IAppContext extends IDatabaseContext {
|
|
149
|
+
jwt: IJWTPayload;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
- Les appels devant faire partie d'une même transaction sont exécutés dans le "scope" de
|
|
153
|
+
`txManager.withTransaction(...)`. Le code appellé reçoit un `client knex`, et c'est
|
|
154
|
+
ce client qu'il doit utiliser pour effectuer ses requêtes SQL. Dans notre exemple, le service
|
|
155
|
+
ne fait une seule requête SQL par lui-même: `client("statistics").insert(...)`. Mais il passe
|
|
156
|
+
son `context` à une repossitory et à un autre service pour que la transaction qu'il a démarrée soit
|
|
157
|
+
poursuivie correctement par ces composants.
|
|
158
|
+
|
|
159
|
+
Il faut savoir que la création de l'objet `context` _original_, celui qui devra par la suite être
|
|
160
|
+
passé de méthode en méthode, se fait en général _dans le contrôleur_ qui reçoit une requête HTTP.
|
|
161
|
+
Ce `context` initial aura sa propriété `currentKnexClient` non définie. C'est le premier appel à
|
|
162
|
+
`withClient` ou `withTransaction` qui s'occupera de le populer (puis de le tenir à jour).
|
|
163
|
+
|
|
164
|
+
Notez qu'en plus de `withTransaction`, le composant `KnexTransactionManager` fournit aussi une méthode
|
|
165
|
+
`withClient`. L'utilisation est la même, à la différence près qu'aucune transaction n'est
|
|
166
|
+
démarrée avec `withClient`. Ceci dit, il est possible que la méthode où s'effectue la requête
|
|
167
|
+
ne nécessitant pas de transaction _ait elle-même été appellée au sein d'un `withTransaction`_, bref dans le scope d'une transaction, par du code appelant! Dans cette situation, en utilisant le client knex fourni par `withClient`,
|
|
168
|
+
la transaction se poursuivra correctement. C'est pour cette raison qu'il faut _toujours_ utiliser
|
|
169
|
+
le client knex fourni par `withClient` et non un client knex créé manuellement...
|
|
170
|
+
`withClient` et `withTransaction` inspectent le `context` qui leur est passé et retourne un client
|
|
171
|
+
knex bien configuré, selon la situation.
|
|
172
|
+
|
|
173
|
+
Notez aussi que si `withTransaction` est appellée mais que le code est _déjà_ dans le scope
|
|
174
|
+
d'une transaction démarrée par du code appelant, la transaction déjà existante se poursuivera.
|
|
175
|
+
Il n'y aura pas de nouvelle transaction de démarrée.
|
|
176
|
+
|
|
177
|
+
Notez finalement qu'il peut être intéressant de wrapper les méthodes du `KnexTransactionManager`
|
|
178
|
+
dans des méthodes custom, pour simplifier et modifier leur utilisation. Par exemple:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
public async withTransaction<T>(
|
|
182
|
+
context: IAppContext,
|
|
183
|
+
fnt: (client: knex.Transaction) => Promise<T>
|
|
184
|
+
): Promise<T> {
|
|
185
|
+
return this.getKnexTransactionManager().withTransaction<T>(context, async (client: knex) => {
|
|
186
|
+
try {
|
|
187
|
+
return await fnt(client as any);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
// ==========================================
|
|
190
|
+
// Ici, nous pourrions avoir du code convertissant
|
|
191
|
+
// les erreurs BD en erreur d'API standardisées,
|
|
192
|
+
// par exemple.
|
|
193
|
+
// ==========================================
|
|
194
|
+
throw createErrorFromDatabaseError(err);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Par la suite:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Utilisation de notre méthode `withTransaction` custom.
|
|
204
|
+
await withTransaction(context, async (client: knex.Transaction) => {
|
|
205
|
+
const res = await client(`statistics`).insert({
|
|
206
|
+
something: 'blablabla',
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**NOTE**: Voir le fichier de tests `src/transactionManager.test.ts` pour un exemple d'utilisation
|
|
212
|
+
du `KnexTransactionManager`.
|
|
213
|
+
|
|
214
|
+
# Versions et compatibilité
|
|
215
|
+
|
|
216
|
+
### 5.0.0
|
|
217
|
+
|
|
218
|
+
Utilise la version 5.0.0 de sqlite3
|
|
219
|
+
|
|
220
|
+
#### Compatibilité
|
|
221
|
+
|
|
222
|
+
- NodeJS : 16
|
|
223
|
+
|
|
224
|
+
# Test et publication de la librairie sur Nexus
|
|
225
|
+
|
|
226
|
+
En mergant une pull request dans la branche `develop`, un artifact "`-pre.build`" sera créé automatiquement dans Nexus. Vous
|
|
227
|
+
pouvez utiliser cette version temporaire de la librairie pour bien la tester dans un réel projet.
|
|
228
|
+
|
|
229
|
+
Une fois mergée dans `master`, la librairie est définitiement publiée dans Nexus, en utilisant la version spécifiée dans
|
|
230
|
+
le `package.json`.
|
|
231
|
+
|
|
232
|
+
# Aide / Contributions
|
|
233
|
+
|
|
234
|
+
Pour obtenir de l'aide avec cette librairie, vous pouvez poster sur la salle Google Chat [dev-discussions](https://chat.google.com/room/AAAASmiQveI).
|
|
235
|
+
|
|
236
|
+
Notez que les contributions sous forme de pull requests sont bienvenues.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ILogger } from '@villedemontreal/logger';
|
|
2
|
+
export declare class Configs {
|
|
3
|
+
/**
|
|
4
|
+
* Absolute path to the root of the project.
|
|
5
|
+
*/
|
|
6
|
+
root: string;
|
|
7
|
+
constructor();
|
|
8
|
+
private _loggerCreator;
|
|
9
|
+
/**
|
|
10
|
+
* The Logger creator
|
|
11
|
+
*/
|
|
12
|
+
get loggerCreator(): (name: string) => ILogger;
|
|
13
|
+
/**
|
|
14
|
+
* Sets the Logger creator.
|
|
15
|
+
*/
|
|
16
|
+
setLoggerCreator(loggerCreator: (name: string) => ILogger): void;
|
|
17
|
+
}
|
|
18
|
+
export declare const configs: Configs;
|
|
19
|
+
//# sourceMappingURL=configs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configs.d.ts","sourceRoot":"","sources":["../../../src/config/configs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAGlD,qBAAa,OAAO;IAClB;;OAEG;IACI,IAAI,EAAE,MAAM,CAAC;;IAMpB,OAAO,CAAC,cAAc,CAA4B;IAElD;;OAEG;IACH,IAAI,aAAa,IAAI,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAK7C;IAED;;OAEG;IACI,gBAAgB,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO;CAGjE;AAED,eAAO,MAAM,OAAO,EAAE,OAAuB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configs = exports.Configs = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
class Configs {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.root = path.normalize(`${__dirname}/../../..`);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The Logger creator
|
|
11
|
+
*/
|
|
12
|
+
get loggerCreator() {
|
|
13
|
+
if (!this._loggerCreator) {
|
|
14
|
+
throw new Error(`The Logger Creator HAS to be set as a configuration`);
|
|
15
|
+
}
|
|
16
|
+
return this._loggerCreator;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sets the Logger creator.
|
|
20
|
+
*/
|
|
21
|
+
setLoggerCreator(loggerCreator) {
|
|
22
|
+
this._loggerCreator = loggerCreator;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.Configs = Configs;
|
|
26
|
+
exports.configs = new Configs();
|
|
27
|
+
//# sourceMappingURL=configs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configs.js","sourceRoot":"","sources":["../../../src/config/configs.ts"],"names":[],"mappings":";;;AACA,6BAA6B;AAE7B,MAAa,OAAO;IAMlB;QACE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,SAAS,WAAW,CAAC,CAAC;IACtD,CAAC;IAID;;OAEG;IACH,IAAI,aAAa;QACf,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED;;OAEG;IACI,gBAAgB,CAAC,aAAwC;QAC9D,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;IACtC,CAAC;CACF;AA5BD,0BA4BC;AAEY,QAAA,OAAO,GAAY,IAAI,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application constants
|
|
3
|
+
*/
|
|
4
|
+
export declare class Constants {
|
|
5
|
+
/**
|
|
6
|
+
* The library root. When this library is used
|
|
7
|
+
* as a dependency in a project, the "libRoot"
|
|
8
|
+
* will be the path to the dependency folder,
|
|
9
|
+
* inside the "node_modules".
|
|
10
|
+
*/
|
|
11
|
+
libRoot: string;
|
|
12
|
+
/**
|
|
13
|
+
* The app root. When this library is used
|
|
14
|
+
* as a dependency in a project, the "appRoot"
|
|
15
|
+
* will be the path to the root project!
|
|
16
|
+
*/
|
|
17
|
+
appRoot: string;
|
|
18
|
+
constructor();
|
|
19
|
+
}
|
|
20
|
+
export declare const constants: Constants;
|
|
21
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/config/constants.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,qBAAa,SAAS;IACpB;;;;;OAKG;IACI,OAAO,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACI,OAAO,EAAE,MAAM,CAAC;;CAOxB;AAED,eAAO,MAAM,SAAS,EAAE,SAA2B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.constants = exports.Constants = void 0;
|
|
4
|
+
// ==========================================
|
|
5
|
+
// Application constants
|
|
6
|
+
// ==========================================
|
|
7
|
+
const app_root_path_1 = require("app-root-path");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
/**
|
|
10
|
+
* Application constants
|
|
11
|
+
*/
|
|
12
|
+
class Constants {
|
|
13
|
+
constructor() {
|
|
14
|
+
// From the "dist/src/config" folder
|
|
15
|
+
this.libRoot = path.normalize(__dirname + '/../../..');
|
|
16
|
+
this.appRoot = app_root_path_1.path;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.Constants = Constants;
|
|
20
|
+
exports.constants = new Constants();
|
|
21
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/config/constants.ts"],"names":[],"mappings":";;;AAAA,6CAA6C;AAC7C,wBAAwB;AACxB,6CAA6C;AAC7C,iDAAgD;AAChD,6BAA6B;AAE7B;;GAEG;AACH,MAAa,SAAS;IAgBpB;QACE,oCAAoC;QACpC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC;QACvD,IAAI,CAAC,OAAO,GAAG,oBAAO,CAAC;IACzB,CAAC;CACF;AArBD,8BAqBC;AAEY,QAAA,SAAS,GAAc,IAAI,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ILogger } from '@villedemontreal/logger';
|
|
2
|
+
/**
|
|
3
|
+
* Inits the library.
|
|
4
|
+
*/
|
|
5
|
+
export declare function init(loggerCreator: (name: string) => ILogger): void;
|
|
6
|
+
/**
|
|
7
|
+
* Is the library properly initialized?
|
|
8
|
+
*
|
|
9
|
+
* This function MUST be named "isInited()"!
|
|
10
|
+
* Code using this library may loop over all its "@villemontreal"
|
|
11
|
+
* dependencies and, if one of those exports a "isInited" fonction,
|
|
12
|
+
* it will enforce that the lib has been properly initialized before
|
|
13
|
+
* starting...
|
|
14
|
+
*/
|
|
15
|
+
export declare function isInited(): boolean;
|
|
16
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/config/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAKlD;;GAEG;AACH,wBAAgB,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,QAW5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,IAAI,OAAO,CAElC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.init = init;
|
|
4
|
+
exports.isInited = isInited;
|
|
5
|
+
const configs_1 = require("./configs");
|
|
6
|
+
let libIsInited = false;
|
|
7
|
+
/**
|
|
8
|
+
* Inits the library.
|
|
9
|
+
*/
|
|
10
|
+
function init(loggerCreator) {
|
|
11
|
+
if (!loggerCreator) {
|
|
12
|
+
throw new Error(`The Logger Creator is required.`);
|
|
13
|
+
}
|
|
14
|
+
configs_1.configs.setLoggerCreator(loggerCreator);
|
|
15
|
+
// ==========================================
|
|
16
|
+
// Set as being "properly initialized".
|
|
17
|
+
// At the very end of the "init()" function!
|
|
18
|
+
// ==========================================
|
|
19
|
+
libIsInited = true;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Is the library properly initialized?
|
|
23
|
+
*
|
|
24
|
+
* This function MUST be named "isInited()"!
|
|
25
|
+
* Code using this library may loop over all its "@villemontreal"
|
|
26
|
+
* dependencies and, if one of those exports a "isInited" fonction,
|
|
27
|
+
* it will enforce that the lib has been properly initialized before
|
|
28
|
+
* starting...
|
|
29
|
+
*/
|
|
30
|
+
function isInited() {
|
|
31
|
+
return libIsInited;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/config/init.ts"],"names":[],"mappings":";;AAQA,oBAWC;AAWD,4BAEC;AA/BD,uCAAoC;AAEpC,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB;;GAEG;AACH,SAAgB,IAAI,CAAC,aAAwC;IAC3D,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IACD,iBAAO,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;IAExC,6CAA6C;IAC7C,uCAAuC;IACvC,4CAA4C;IAC5C,6CAA6C;IAC7C,WAAW,GAAG,IAAI,CAAC;AACrB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,QAAQ;IACtB,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* A Context that can be used to get the current database
|
|
4
|
+
* client, if any. This allows the use of nested transactions.
|
|
5
|
+
*/
|
|
6
|
+
export interface IDatabaseContext {
|
|
7
|
+
currentKnexClient?: Knex;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=databaseContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"databaseContext.d.ts","sourceRoot":"","sources":["../../src/databaseContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,CAAC,EAAE,IAAI,CAAC;CAC1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"databaseContext.js","sourceRoot":"","sources":["../../src/databaseContext.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
// ==========================================
|
|
18
|
+
// We do not export the configs instance itself,
|
|
19
|
+
// only the "init()" method, so we can define
|
|
20
|
+
// which are the required parameters.
|
|
21
|
+
// ==========================================
|
|
22
|
+
__exportStar(require("./config/init"), exports);
|
|
23
|
+
__exportStar(require("./databaseContext"), exports);
|
|
24
|
+
__exportStar(require("./knexUtils"), exports);
|
|
25
|
+
__exportStar(require("./transactionManager"), exports);
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA6C;AAC7C,gDAAgD;AAChD,6CAA6C;AAC7C,qCAAqC;AACrC,6CAA6C;AAC7C,gDAA8B;AAC9B,oDAAkC;AAClC,8CAA4B;AAC5B,uDAAqC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { IPaginatedResult } from '@villedemontreal/general-utils';
|
|
2
|
+
import { Knex } from 'knex';
|
|
3
|
+
import * as sinon from 'sinon';
|
|
4
|
+
/**
|
|
5
|
+
* Knex utilities
|
|
6
|
+
*/
|
|
7
|
+
export declare class KnexUtils {
|
|
8
|
+
protected static KNEX_TOTAL_COUNT_QUERY_COLUMN_NAME: string;
|
|
9
|
+
protected static TOTAL_COUNT_BUILDER_OPTION_NAME: string;
|
|
10
|
+
/**
|
|
11
|
+
* Takes a Knex.QueryBuilder, which is the object created
|
|
12
|
+
* when defining a Knex query, an returns the rows total count.
|
|
13
|
+
*
|
|
14
|
+
* This function is useful when you already have a SELECT Knex query,
|
|
15
|
+
* but you only need the rows total count instead of the rows themselves!
|
|
16
|
+
*
|
|
17
|
+
* Warning!! This function only works with SELECT queries, and
|
|
18
|
+
* may fail in some untested complex situations... It only has
|
|
19
|
+
* been tested with simple select/from/where/orderBy queries.
|
|
20
|
+
*
|
|
21
|
+
* Example :
|
|
22
|
+
*
|
|
23
|
+
* const queryBuilder = client
|
|
24
|
+
* .from(BOOKS_TABLE_NAME)
|
|
25
|
+
* .orderBy("author");
|
|
26
|
+
*
|
|
27
|
+
* ... and then, instead of executing the query, by using
|
|
28
|
+
* "then()" or "await", you pass the builder to the
|
|
29
|
+
* "totalCount()" function :
|
|
30
|
+
*
|
|
31
|
+
* let totalCount: number = await knexUtils.totalCount(client, queryBuilder);
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
totalCount(knex: Knex, selectBuilder: Knex.QueryBuilder): Promise<number>;
|
|
35
|
+
/**
|
|
36
|
+
* Takes a Knex.QueryBuilder, which is the object created
|
|
37
|
+
* when defining a Knex query, a limit and a current
|
|
38
|
+
* page, then return a IPaginatedResult.
|
|
39
|
+
*
|
|
40
|
+
* In other words, instead of executing the query you
|
|
41
|
+
* are building with Knex directly, you pass the unsent
|
|
42
|
+
* builder to this function and you get :
|
|
43
|
+
* - Pagination for your query
|
|
44
|
+
* - The total number of elements your query would return if it
|
|
45
|
+
* wasn't paginated.
|
|
46
|
+
*
|
|
47
|
+
* Warning!! This function only works with SELECT queries, and
|
|
48
|
+
* may fail in some untested complex situations... It only has
|
|
49
|
+
* been tested with simple select/from/where/orderBy queries.
|
|
50
|
+
*
|
|
51
|
+
* If this function fails for one of your query, you'll have to
|
|
52
|
+
* duplicate the code of that query to make two separate queries :
|
|
53
|
+
* one for the rows only and one for the total count only.
|
|
54
|
+
*
|
|
55
|
+
* For example, to get 3 items starting at offset 9 :
|
|
56
|
+
*
|
|
57
|
+
* const paginatedResult = await knexUtils.paginate(
|
|
58
|
+
* client,
|
|
59
|
+
* client
|
|
60
|
+
* .select("id", "author", "title")
|
|
61
|
+
* .from(BOOKS_TABLE_NAME)
|
|
62
|
+
* .orderBy("author"),
|
|
63
|
+
* 9, 3);
|
|
64
|
+
*/
|
|
65
|
+
paginate(knex: Knex, selectBuilder: Knex.QueryBuilder, offset: number, limit: number): Promise<IPaginatedResult<any>>;
|
|
66
|
+
/**
|
|
67
|
+
* Creates a mocked Knex client, linked to a dummy database.
|
|
68
|
+
* The client allows you to define stubs that will simulate
|
|
69
|
+
* the result from the DB.
|
|
70
|
+
*
|
|
71
|
+
* Useful for testing!
|
|
72
|
+
*/
|
|
73
|
+
createKnexMockedClient: () => Promise<IKnexMockedClient>;
|
|
74
|
+
/**
|
|
75
|
+
* For Oracle.
|
|
76
|
+
* Wraps a column name (or a "?") with LOWER or with a CONVERT function
|
|
77
|
+
* which will strip accents.
|
|
78
|
+
*/
|
|
79
|
+
wrapWithOracleModificationkeywords(columnNameOrInterrogationMark: string, isConvert: boolean, isLower: boolean): string;
|
|
80
|
+
/**
|
|
81
|
+
* For Oracle.
|
|
82
|
+
* Adds a LIKE clause, where the values can be compared lowercased (isLower), by removing the
|
|
83
|
+
* accents first (isConvert), and where the "val" can starts or ends with a "*" wildcard.
|
|
84
|
+
*/
|
|
85
|
+
addOracleLikeClause(queryBuilder: Knex.QueryBuilder, columnName: string, val: string, isConvert: boolean, isLower: boolean): Knex.QueryBuilder;
|
|
86
|
+
/**
|
|
87
|
+
* For SQL Server.
|
|
88
|
+
* Wraps a column name (or a "?") with LOWER and/or with a CAST function
|
|
89
|
+
* which will strip accents.
|
|
90
|
+
*/
|
|
91
|
+
wrapWithSqlServerModificationKeywords(columnNameOrInterrogationMark: string, isConvert: boolean, isLower: boolean): string;
|
|
92
|
+
/**
|
|
93
|
+
* For SQL Server.
|
|
94
|
+
* Adds a LIKE clause, where the values can be compared lowercased (lower), by removing the
|
|
95
|
+
* accents first (removeAccent), and where the "val" can starts or ends with a "*" wildcard
|
|
96
|
+
* (acceptWildcard).
|
|
97
|
+
*/
|
|
98
|
+
addSqlServerLikeClause(queryBuilder: Knex.QueryBuilder, columnName: string, val: string, acceptWildcard: boolean, removeAccents: boolean, lower: boolean): Knex.QueryBuilder;
|
|
99
|
+
/**
|
|
100
|
+
* @param totalCountOnly if true, only the request to get the total count will
|
|
101
|
+
* be made and an empty array will be returned as the rows.
|
|
102
|
+
*/
|
|
103
|
+
protected paginateOrTotalCount(knex: Knex, selectBuilder: Knex.QueryBuilder, offset: number, limit: number, totalCountOnly?: boolean): Promise<IPaginatedResult<any>>;
|
|
104
|
+
}
|
|
105
|
+
export declare const knexUtils: KnexUtils;
|
|
106
|
+
/**
|
|
107
|
+
* A Mocked Knex client.
|
|
108
|
+
*/
|
|
109
|
+
export interface IKnexMockedClient extends Knex {
|
|
110
|
+
/**
|
|
111
|
+
* The stub that is going to return the result
|
|
112
|
+
* when the query is executed.
|
|
113
|
+
*
|
|
114
|
+
* Defaults to an empty array.
|
|
115
|
+
*/
|
|
116
|
+
resultStub: sinon.SinonStub;
|
|
117
|
+
/**
|
|
118
|
+
* If the query is paginated, this stub
|
|
119
|
+
* will return the "totalCount" part of
|
|
120
|
+
* the result.
|
|
121
|
+
*
|
|
122
|
+
* Defaults to 0.
|
|
123
|
+
*/
|
|
124
|
+
totalCountStub: sinon.SinonStub;
|
|
125
|
+
/**
|
|
126
|
+
* This spy is going to be called just before
|
|
127
|
+
* Knex actually execute the query. The builder
|
|
128
|
+
* has at this point been converted to a
|
|
129
|
+
* SQL string and you have access to this SQL and
|
|
130
|
+
* other informations.
|
|
131
|
+
*/
|
|
132
|
+
beforeQuerySpy: sinon.SinonSpy;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=knexUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knexUtils.d.ts","sourceRoot":"","sources":["../../src/knexUtils.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAS,MAAM,gCAAgC,CAAC;AAEzE,OAAa,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAElC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B;;GAEG;AACH,qBAAa,SAAS;IACpB,SAAS,CAAC,MAAM,CAAC,kCAAkC,SAAW;IAC9D,SAAS,CAAC,MAAM,CAAC,+BAA+B,SAAyB;IAEzE;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACU,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAKtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACU,QAAQ,CACnB,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,IAAI,CAAC,YAAY,EAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAKjC;;;;;;OAMG;IACI,sBAAsB,QAAa,OAAO,CAAC,iBAAiB,CAAC,CAsFlE;IAEF;;;;OAIG;IACI,kCAAkC,CACvC,6BAA6B,EAAE,MAAM,EACrC,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,OAAO,GACf,MAAM;IAaT;;;;OAIG;IACI,mBAAmB,CACxB,YAAY,EAAE,IAAI,CAAC,YAAY,EAC/B,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,OAAO,GACf,IAAI,CAAC,YAAY;IAgCpB;;;;OAIG;IACI,qCAAqC,CAC1C,6BAA6B,EAAE,MAAM,EACrC,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,OAAO,GACf,MAAM;IA0BT;;;;;OAKG;IACI,sBAAsB,CAC3B,YAAY,EAAE,IAAI,CAAC,YAAY,EAC/B,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,OAAO,EACvB,aAAa,EAAE,OAAO,EACtB,KAAK,EAAE,OAAO,GACb,IAAI,CAAC,YAAY;IAmCpB;;;OAGG;cACa,oBAAoB,CAClC,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,IAAI,CAAC,YAAY,EAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,cAAc,UAAQ,GACrB,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;CAiFlC;AACD,eAAO,MAAM,SAAS,EAAE,SAA2B,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI;IAC7C;;;;;OAKG;IACH,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC;IAE5B;;;;;;OAMG;IACH,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC;IAEhC;;;;;;OAMG;IACH,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC;CAChC"}
|