@villedemontreal/jwt-validator 5.7.7
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/LICENSE +21 -0
- package/README.md +313 -0
- package/dist/scripts/index.d.ts +6 -0
- package/dist/scripts/index.js +16 -0
- package/dist/scripts/index.js.map +1 -0
- package/dist/scripts/lint.d.ts +6 -0
- package/dist/scripts/lint.js +18 -0
- package/dist/scripts/lint.js.map +1 -0
- package/dist/scripts/lintFix.d.ts +6 -0
- package/dist/scripts/lintFix.js +21 -0
- package/dist/scripts/lintFix.js.map +1 -0
- package/dist/scripts/showCoverage.d.ts +13 -0
- package/dist/scripts/showCoverage.js +40 -0
- package/dist/scripts/showCoverage.js.map +1 -0
- package/dist/scripts/test.d.ts +13 -0
- package/dist/scripts/test.js +29 -0
- package/dist/scripts/test.js.map +1 -0
- package/dist/scripts/testUnits.d.ts +15 -0
- package/dist/scripts/testUnits.js +95 -0
- package/dist/scripts/testUnits.js.map +1 -0
- package/dist/scripts/watch.d.ts +14 -0
- package/dist/scripts/watch.js +96 -0
- package/dist/scripts/watch.js.map +1 -0
- package/dist/src/config/configs.d.ts +88 -0
- package/dist/src/config/configs.js +123 -0
- package/dist/src/config/configs.js.map +1 -0
- package/dist/src/config/constants.d.ts +56 -0
- package/dist/src/config/constants.js +66 -0
- package/dist/src/config/constants.js.map +1 -0
- package/dist/src/config/init.d.ts +15 -0
- package/dist/src/config/init.js +48 -0
- package/dist/src/config/init.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +32 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/jwtValidator.d.ts +21 -0
- package/dist/src/jwtValidator.js +129 -0
- package/dist/src/jwtValidator.js.map +1 -0
- package/dist/src/jwtValidator.test.d.ts +1 -0
- package/dist/src/jwtValidator.test.js +500 -0
- package/dist/src/jwtValidator.test.js.map +1 -0
- package/dist/src/middleware/jwtMiddleware.d.ts +7 -0
- package/dist/src/middleware/jwtMiddleware.js +27 -0
- package/dist/src/middleware/jwtMiddleware.js.map +1 -0
- package/dist/src/models/customError.d.ts +11 -0
- package/dist/src/models/customError.js +38 -0
- package/dist/src/models/customError.js.map +1 -0
- package/dist/src/models/expressRequest.d.ts +15 -0
- package/dist/src/models/expressRequest.js +17 -0
- package/dist/src/models/expressRequest.js.map +1 -0
- package/dist/src/models/gluuUserType.d.ts +9 -0
- package/dist/src/models/gluuUserType.js +14 -0
- package/dist/src/models/gluuUserType.js.map +1 -0
- package/dist/src/models/jwtPayload.d.ts +30 -0
- package/dist/src/models/jwtPayload.js +19 -0
- package/dist/src/models/jwtPayload.js.map +1 -0
- package/dist/src/models/pagination.d.ts +16 -0
- package/dist/src/models/pagination.js +16 -0
- package/dist/src/models/pagination.js.map +1 -0
- package/dist/src/models/publicKey.d.ts +29 -0
- package/dist/src/models/publicKey.js +13 -0
- package/dist/src/models/publicKey.js.map +1 -0
- package/dist/src/repositories/cachedPublicKeyRepository.d.ts +53 -0
- package/dist/src/repositories/cachedPublicKeyRepository.js +102 -0
- package/dist/src/repositories/cachedPublicKeyRepository.js.map +1 -0
- package/dist/src/repositories/publicKeyRepository.d.ts +19 -0
- package/dist/src/repositories/publicKeyRepository.js +44 -0
- package/dist/src/repositories/publicKeyRepository.js.map +1 -0
- package/dist/src/userValidator.d.ts +30 -0
- package/dist/src/userValidator.js +35 -0
- package/dist/src/userValidator.js.map +1 -0
- package/dist/src/userValidator.test.d.ts +1 -0
- package/dist/src/userValidator.test.js +251 -0
- package/dist/src/userValidator.test.js.map +1 -0
- package/dist/src/utils/jwtMock.d.ts +31 -0
- package/dist/src/utils/jwtMock.js +221 -0
- package/dist/src/utils/jwtMock.js.map +1 -0
- package/dist/src/utils/logger.d.ts +11 -0
- package/dist/src/utils/logger.js +54 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/testingConfigurations.d.ts +7 -0
- package/dist/src/utils/testingConfigurations.js +16 -0
- package/dist/src/utils/testingConfigurations.js.map +1 -0
- package/package.json +82 -0
- package/src/config/configs.ts +145 -0
- package/src/config/constants.ts +83 -0
- package/src/config/init.ts +58 -0
- package/src/index.ts +15 -0
- package/src/jwtValidator.test.ts +607 -0
- package/src/jwtValidator.ts +162 -0
- package/src/middleware/jwtMiddleware.ts +33 -0
- package/src/models/customError.ts +37 -0
- package/src/models/expressRequest.ts +27 -0
- package/src/models/gluuUserType.ts +9 -0
- package/src/models/jwtPayload.ts +58 -0
- package/src/models/pagination.ts +26 -0
- package/src/models/publicKey.ts +33 -0
- package/src/repositories/cachedPublicKeyRepository.ts +121 -0
- package/src/repositories/publicKeyRepository.ts +75 -0
- package/src/userValidator.test.ts +279 -0
- package/src/userValidator.ts +54 -0
- package/src/utils/jwtMock.ts +243 -0
- package/src/utils/logger.ts +60 -0
- package/src/utils/testingConfigurations.ts +12 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Ville de Montréal and other contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# @villedemontreal/jwt-validator
|
|
2
|
+
|
|
3
|
+
Module to validate JWT generated by Kong
|
|
4
|
+
|
|
5
|
+
## Availabililty
|
|
6
|
+
|
|
7
|
+
https://bitbucket.org/villemontreal/core-jwt-validator-nodejs-lib
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```shell
|
|
12
|
+
npm install --save @villedemontreal/jwt-validator
|
|
13
|
+
yarn add @villedemontreal/jwt-validator
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### Initialisation
|
|
19
|
+
|
|
20
|
+
Un code utilisant cette librarie doit premièrement la configurer en appellant la fonction
|
|
21
|
+
"`ìnit(...)`" exportée par le fichier "`src/config/init.ts`".
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { init as initJwtValidationLib } from '@villedemontreal/jwt-validator';
|
|
25
|
+
import { createLogger } from './utils/logger';
|
|
26
|
+
import { correlationIdService, init as initCidUtils } from '@villedemontreal/correlation-id';
|
|
27
|
+
|
|
28
|
+
// ...
|
|
29
|
+
|
|
30
|
+
export async function initComponents() {
|
|
31
|
+
initJwtValidationLib(
|
|
32
|
+
createLogger,
|
|
33
|
+
() => {
|
|
34
|
+
return correlationIdService.getId();
|
|
35
|
+
},
|
|
36
|
+
configs.security.jwt.host, // Optional: already defined
|
|
37
|
+
configs.security.jwt.endPoint // Optional: already defined
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
//...
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Notez qu'une fonction "`isInited()`" est exportée et permet au code appelant de valider que la librairie a été
|
|
45
|
+
configurée correctement!
|
|
46
|
+
|
|
47
|
+
### JWT Validation With Middleware
|
|
48
|
+
|
|
49
|
+
Route definition:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { jwtValidationMiddleware } from '@villedemontreal/jwt-validator';
|
|
53
|
+
|
|
54
|
+
export function getAPIRoutes(): IHandlerRoute[] {
|
|
55
|
+
return [
|
|
56
|
+
// Get Account Information
|
|
57
|
+
{
|
|
58
|
+
method: HttpMethods.GET,
|
|
59
|
+
path: '/v1/accounts/:id',
|
|
60
|
+
handler: accountsController.getAccount,
|
|
61
|
+
middlewares: [jwtValidationMiddleware()]
|
|
62
|
+
}
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Global definition in `app.ts`:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
export async function createApp(apiRoutes: IHandlerRoute[]): Promise<express.Express> {
|
|
71
|
+
// ...
|
|
72
|
+
|
|
73
|
+
if (configs.security.jwt.enable) {
|
|
74
|
+
let jwtMiddleware = jwtValidationMiddleware();
|
|
75
|
+
app.use(function(req: express.Request, res: express.Response, next: express.NextFunction) {
|
|
76
|
+
if (req && req.path && req.path.toLowerCase().startsWith(constants.EnpointTypeRoots.API)) {
|
|
77
|
+
jwtMiddleware(req, res, next);
|
|
78
|
+
} else {
|
|
79
|
+
next();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ...
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### User validation
|
|
89
|
+
|
|
90
|
+
Then in the controller, it's possible to check the ID inside the JWT:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { UserValidator } from "@villedemontreal/jwt-validator";
|
|
94
|
+
|
|
95
|
+
// Controller to update an account: PUT /accounts/:inum
|
|
96
|
+
public async update(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
|
97
|
+
|
|
98
|
+
// Get inum from request
|
|
99
|
+
let inum: string = req.params.id;
|
|
100
|
+
|
|
101
|
+
let userValidator: UserValidator = new UserValidator(req);
|
|
102
|
+
|
|
103
|
+
// Return if the ID is the same or not
|
|
104
|
+
let same: boolean = userValidator.isUser(inum);
|
|
105
|
+
|
|
106
|
+
// Throw an exception if the ID is not the same
|
|
107
|
+
userValidator.verifyUser(inum);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Get JWT content
|
|
112
|
+
|
|
113
|
+
To get the JWT content, you have to cast the `req` variable with the custom type `Request` .
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { IJWTPayload, IRequestWithJwt } from "@villedemontreal/jwt-validator";
|
|
117
|
+
|
|
118
|
+
// Controller to update an account: PUT /accounts/:inum
|
|
119
|
+
public async update(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
|
120
|
+
|
|
121
|
+
// Get JWT content
|
|
122
|
+
let jwtPayload: IJWTPayload = (<IRequestWithJwt>req).jwt;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Manual JWT Validation
|
|
127
|
+
|
|
128
|
+
Validate a JWT and a JWT and the ID inside the JWT
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { jwtValidator, IJWTPayload } from "@villedemontreal/jwt-validator";
|
|
132
|
+
|
|
133
|
+
// Controller to update an account: PUT /accounts/:inum
|
|
134
|
+
public async update(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
|
135
|
+
|
|
136
|
+
// Get inum from request
|
|
137
|
+
let inum: string = req.params.id;
|
|
138
|
+
|
|
139
|
+
// Validate Authorization header, check inum in JWT and get JWT content
|
|
140
|
+
let jwtPayload: IJWTPayload = await jwtValidator.verifyAuthorizationHeader(req.header('Authorization'));
|
|
141
|
+
|
|
142
|
+
// Check the ID inside the JWT
|
|
143
|
+
assert.strictEqual(jwtPayload.sub, inum);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
It is also possible to check to validate directly the JWT value:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { jwtValidator, IJWTPayload } from '@villedemontreal/jwt-validator';
|
|
151
|
+
|
|
152
|
+
let jwt: string = '...';
|
|
153
|
+
|
|
154
|
+
// Validate JWT and get JWT content
|
|
155
|
+
let jwtPayload: IJWTPayload = await jwtValidator.verifyToken(jwt);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Usage For Tests
|
|
159
|
+
|
|
160
|
+
This library provides a mock tool to generate your own JWT with a signature which it will be correcly validated.
|
|
161
|
+
|
|
162
|
+
There are two parts to do this:
|
|
163
|
+
|
|
164
|
+
- The method `mockPublicKeys`, clean the public keys cache and intercept the first request to get the public keys. Then, our mock keys are inserted in the pubic keys cache.
|
|
165
|
+
|
|
166
|
+
- The method `generateJwt` allows to produce your own JWT signed with a private key.
|
|
167
|
+
|
|
168
|
+
Example of tests with mocha:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { jwtMock } from '@villedemontreal/jwt-validator';
|
|
172
|
+
|
|
173
|
+
describe('Test', function() {
|
|
174
|
+
before(async function() {
|
|
175
|
+
// Mock the public keys
|
|
176
|
+
await jwtMock.mockPublicKeys();
|
|
177
|
+
|
|
178
|
+
// Generate JWT
|
|
179
|
+
let jwtToken = jwtMock.generateJwt();
|
|
180
|
+
|
|
181
|
+
// Generate a JWT with your custom value:
|
|
182
|
+
let jwtToken = jwtMock.generateJwt({
|
|
183
|
+
accessToken: 'c9ba5a95-d7f9-41f9-9a24-a7e41882f7ef',
|
|
184
|
+
iss: 'jwt-mock',
|
|
185
|
+
|
|
186
|
+
// From Introspect
|
|
187
|
+
exp: Date.now() + 3600,
|
|
188
|
+
iat: Date.now(),
|
|
189
|
+
// Use this keyId to have a valid public key
|
|
190
|
+
keyId: 5,
|
|
191
|
+
/*
|
|
192
|
+
Key ID:
|
|
193
|
+
- 1: Public Key expired with the status as "expired"
|
|
194
|
+
- 2: Public Key expired WITHOUT the status as "expired"
|
|
195
|
+
- 3: Public Key revoked
|
|
196
|
+
- 4: Public Key still active but an expiration date is set
|
|
197
|
+
- 5: Public Key active without an expiration date
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
// From ClientInfo
|
|
201
|
+
displayName: 'Service Account',
|
|
202
|
+
aud: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0008!2212.0010',
|
|
203
|
+
|
|
204
|
+
// From UserInfo
|
|
205
|
+
name: 'Guillaume Smaha',
|
|
206
|
+
sub: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0000!0000.0001',
|
|
207
|
+
inum: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0000!0000.0001',
|
|
208
|
+
userName: 'xsmahgu@ville.montreal.qc.ca',
|
|
209
|
+
givenName: 'Guillaume',
|
|
210
|
+
familyName: 'Smaha',
|
|
211
|
+
|
|
212
|
+
customData: {
|
|
213
|
+
// Role, permission, ...
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should accept JWT and update password user', async function() {
|
|
219
|
+
let payload: any = {
|
|
220
|
+
oldPassword: 'testTest2',
|
|
221
|
+
password: 'testTest3'
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
let response = await request(testApp)
|
|
225
|
+
.put(`/api${configs.api.domainPath}/v1/accounts/${userInum}`)
|
|
226
|
+
.set('Authorization', 'Bearer ' + jwtToken)
|
|
227
|
+
.send(payload);
|
|
228
|
+
|
|
229
|
+
assert.strictEqual(response.status, 200);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
# Builder le projet
|
|
235
|
+
|
|
236
|
+
**Note**: Sur Linux/Mac assurz-vous que le fichier `run` est exécutable. Autrement, lancez `chmod +x ./run`.
|
|
237
|
+
|
|
238
|
+
Pour lancer le build :
|
|
239
|
+
|
|
240
|
+
- > `run compile` ou `./run compile` (sur Linux/Mac)
|
|
241
|
+
|
|
242
|
+
Pour lancer les tests :
|
|
243
|
+
|
|
244
|
+
- > `run test` ou `./run test` (sur Linux/Mac)
|
|
245
|
+
|
|
246
|
+
# Mode Watch
|
|
247
|
+
|
|
248
|
+
Lors du développement, il est possible de lancer `run watch` (ou `./run watch` sur Linux/mac) dans un terminal
|
|
249
|
+
externe pour démarrer la compilation incrémentale. Il est alors possible de lancer certaines _launch configuration_
|
|
250
|
+
comme `Debug current tests file - fast` dans VsCode et ainsi déboguer le fichier de tests présentement ouvert sans
|
|
251
|
+
avoir à (re)compiler au préalable (la compilation incrémentale s'en sera chargé).
|
|
252
|
+
|
|
253
|
+
Notez que, par défaut, des _notifications desktop_ sont activées pour indiquer visuellement si la compilation
|
|
254
|
+
incrémentale est un succès ou si une erreur a été trouvée. Vous pouvez désactiver ces notifications en utilisant
|
|
255
|
+
`run watch --dn` (`d`isable `n`otifications).
|
|
256
|
+
|
|
257
|
+
# Déboguer le projet
|
|
258
|
+
|
|
259
|
+
Trois "_launch configurations_" sont founies pour déboguer le projet dans VSCode :
|
|
260
|
+
|
|
261
|
+
- "`Debug all tests`", la launch configuration par défaut. Lance les tests en mode debug. Vous pouvez mettre
|
|
262
|
+
des breakpoints et ils seront respectés.
|
|
263
|
+
|
|
264
|
+
- "`Debug a test file`". Lance _un_ fichier de tests en mode debug. Vous pouvez mettre
|
|
265
|
+
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`".
|
|
266
|
+
|
|
267
|
+
- "`Debug current tests file`". Lance le fichier de tests _présentement ouvert_ dans VSCode en mode debug. Effectue la compîlation au préalable.
|
|
268
|
+
|
|
269
|
+
- "`Debug current tests file - fast`". Lance le fichier de tests _présentement ouvert_ dans VSCode en mode debug. Aucune compilation
|
|
270
|
+
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)
|
|
271
|
+
|
|
272
|
+
# Test et publication de la librairie sur Nexus
|
|
273
|
+
|
|
274
|
+
En mergant une pull request dans la branche `develop`, un artifact "`-pre.build`" sera créé automatiquement dans Nexus. Vous
|
|
275
|
+
pouvez utiliser cette version temporaire de la librairie pour bien la tester dans un réel projet.
|
|
276
|
+
|
|
277
|
+
Une fois mergée dans `master`, la librairie est définitiement publiée dans Nexus, en utilisant la version spécifiée dans
|
|
278
|
+
le `package.json`.
|
|
279
|
+
|
|
280
|
+
## Artifact Nexus privé, lors du développement
|
|
281
|
+
|
|
282
|
+
Lors du développement d'une nouvelle fonctionnalité, sur une branche `feature`, il peut parfois être
|
|
283
|
+
utile de déployer une version temporaire de la librairie dans Nexus. Ceci permet de bien tester
|
|
284
|
+
l'utilisation de la librairie modifiée dans un vrai projet, ou même dans une autre librairie
|
|
285
|
+
elle-même par la suite utilisée dans un vrai projet.
|
|
286
|
+
|
|
287
|
+
Si le code à tester est terminé et prêt à être mis en commun avec d'autres développeurs, la solution
|
|
288
|
+
de base, comme spécifiée à la section précédante, est de merger sur `develop`: ceci créera
|
|
289
|
+
automatiquement un artifact "`-pre-build`" dans Nexus. Cependant, si le code est encore en développement
|
|
290
|
+
et vous désirez éviter de polluer la branche commune `develop` avec du code temporaire, il y a une
|
|
291
|
+
solution permettant de générer un artifact "`[votre prénom]-pre-build`" temporaire dans Nexus,
|
|
292
|
+
à partir d'une branche `feature` directement:
|
|
293
|
+
|
|
294
|
+
1. Checkoutez votre branche `feature` dans une branche nommée "`nexus`". Ce nom est
|
|
295
|
+
important et correspond à une entrée dans le `Jenkinsfile`.
|
|
296
|
+
2. Une fois sur la branche `nexus`, ajoutez un suffixe "`-[votre prénom]`" à
|
|
297
|
+
la version dans le `package.json`, par exemple: "`5.15.0-roger`".
|
|
298
|
+
Ceci permet d'éviter tout conflit dans Nexus et exprime clairement qu'il
|
|
299
|
+
s'agit d'une version temporaire pour votre développement privé.
|
|
300
|
+
3. Commitez et poussez la branche `nexus`.
|
|
301
|
+
4. Une fois le build Jenkins terminé, un artifact pour votre version aura été
|
|
302
|
+
déployé dans Nexus. Détruire votre branche dans Bitbucket pour permettre aux
|
|
303
|
+
autres developpeurs d'utiliser cette approche.
|
|
304
|
+
|
|
305
|
+
**Notez** que, lors du développement dans une branche `feature`, l'utilisation d'un simple
|
|
306
|
+
`npm link` local peut souvent être suffisant! Mais cette solution a ses limites, par exemple si
|
|
307
|
+
vous désirez tester la librairie modifiée _dans un container Docker_.
|
|
308
|
+
|
|
309
|
+
# Aide / Contributions
|
|
310
|
+
|
|
311
|
+
Pour obtenir de l'aide avec cette librairie, vous pouvez poster sur la salle Google Chat [dev-discussions](https://chat.google.com/room/AAAASmiQveI).
|
|
312
|
+
|
|
313
|
+
Notez que les contributions sous forme de pull requests sont bienvenues.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WatchScript = exports.TestUnitsScript = exports.TestScript = exports.ShowCoverageScript = exports.LintFixScript = exports.LintScript = void 0;
|
|
4
|
+
var lint_1 = require("./lint");
|
|
5
|
+
Object.defineProperty(exports, "LintScript", { enumerable: true, get: function () { return lint_1.LintScript; } });
|
|
6
|
+
var lintFix_1 = require("./lintFix");
|
|
7
|
+
Object.defineProperty(exports, "LintFixScript", { enumerable: true, get: function () { return lintFix_1.LintFixScript; } });
|
|
8
|
+
var showCoverage_1 = require("./showCoverage");
|
|
9
|
+
Object.defineProperty(exports, "ShowCoverageScript", { enumerable: true, get: function () { return showCoverage_1.ShowCoverageScript; } });
|
|
10
|
+
var test_1 = require("./test");
|
|
11
|
+
Object.defineProperty(exports, "TestScript", { enumerable: true, get: function () { return test_1.TestScript; } });
|
|
12
|
+
var testUnits_1 = require("./testUnits");
|
|
13
|
+
Object.defineProperty(exports, "TestUnitsScript", { enumerable: true, get: function () { return testUnits_1.TestUnitsScript; } });
|
|
14
|
+
var watch_1 = require("./watch");
|
|
15
|
+
Object.defineProperty(exports, "WatchScript", { enumerable: true, get: function () { return watch_1.WatchScript; } });
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../scripts/index.ts"],"names":[],"mappings":";;;AAAA,+BAAoC;AAA3B,kGAAA,UAAU,OAAA;AACnB,qCAA0C;AAAjC,wGAAA,aAAa,OAAA;AACtB,+CAAoD;AAA3C,kHAAA,kBAAkB,OAAA;AAC3B,+BAAoC;AAA3B,kGAAA,UAAU,OAAA;AACnB,yCAA8C;AAArC,4GAAA,eAAe,OAAA;AACxB,iCAAsC;AAA7B,oGAAA,WAAW,OAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LintScript = void 0;
|
|
4
|
+
const src_1 = require("@villedemontreal/scripting/dist/src");
|
|
5
|
+
const configs_1 = require("../src/config/configs");
|
|
6
|
+
class LintScript extends src_1.ScriptBase {
|
|
7
|
+
get name() {
|
|
8
|
+
return 'lint';
|
|
9
|
+
}
|
|
10
|
+
get description() {
|
|
11
|
+
return `Run the ESLint validation (including TSLint and Prettier rules).`;
|
|
12
|
+
}
|
|
13
|
+
async main() {
|
|
14
|
+
await this.invokeShellCommand(`${configs_1.configs.libRoot}/node_modules/.bin/eslint`, [configs_1.configs.libRoot]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.LintScript = LintScript;
|
|
18
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../scripts/lint.ts"],"names":[],"mappings":";;;AAAA,6DAAiE;AACjE,mDAAgD;AAEhD,MAAa,UAAW,SAAQ,gBAAU;IACxC,IAAI,IAAI;QACN,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,kEAAkE,CAAC;IAC5E,CAAC;IAES,KAAK,CAAC,IAAI;QAClB,MAAM,IAAI,CAAC,kBAAkB,CAAC,GAAG,iBAAO,CAAC,OAAO,2BAA2B,EAAE,CAAC,iBAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAClG,CAAC;CACF;AAZD,gCAYC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LintFixScript = void 0;
|
|
4
|
+
const src_1 = require("@villedemontreal/scripting/dist/src");
|
|
5
|
+
const configs_1 = require("../src/config/configs");
|
|
6
|
+
class LintFixScript extends src_1.ScriptBase {
|
|
7
|
+
get name() {
|
|
8
|
+
return 'lint-fix';
|
|
9
|
+
}
|
|
10
|
+
get description() {
|
|
11
|
+
return `Fix the code using ESLint validation (including TSLint and Prettier rules).`;
|
|
12
|
+
}
|
|
13
|
+
async main() {
|
|
14
|
+
await this.invokeShellCommand(`${configs_1.configs.libRoot}/node_modules/.bin/eslint`, [
|
|
15
|
+
'--fix',
|
|
16
|
+
configs_1.configs.libRoot,
|
|
17
|
+
]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.LintFixScript = LintFixScript;
|
|
21
|
+
//# sourceMappingURL=lintFix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lintFix.js","sourceRoot":"","sources":["../../scripts/lintFix.ts"],"names":[],"mappings":";;;AAAA,6DAAiE;AACjE,mDAAgD;AAEhD,MAAa,aAAc,SAAQ,gBAAU;IAC3C,IAAI,IAAI;QACN,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,6EAA6E,CAAC;IACvF,CAAC;IAES,KAAK,CAAC,IAAI;QAClB,MAAM,IAAI,CAAC,kBAAkB,CAAC,GAAG,iBAAO,CAAC,OAAO,2BAA2B,EAAE;YAC3E,OAAO;YACP,iBAAO,CAAC,OAAO;SAChB,CAAC,CAAC;IACL,CAAC;CACF;AAfD,sCAeC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@caporal/core';
|
|
2
|
+
import { ScriptBase } from '@villedemontreal/scripting/dist/src';
|
|
3
|
+
export interface Options {
|
|
4
|
+
report?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ShowCoverageScript extends ScriptBase<Options> {
|
|
7
|
+
get name(): string;
|
|
8
|
+
get description(): string;
|
|
9
|
+
protected get requiredDependencies(): string[];
|
|
10
|
+
protected configure(command: Command): Promise<void>;
|
|
11
|
+
protected main(): Promise<void>;
|
|
12
|
+
protected getReportDir(): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ShowCoverageScript = void 0;
|
|
4
|
+
const core_1 = require("@caporal/core");
|
|
5
|
+
const src_1 = require("@villedemontreal/scripting/dist/src");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const configs_1 = require("../src/config/configs");
|
|
8
|
+
class ShowCoverageScript extends src_1.ScriptBase {
|
|
9
|
+
get name() {
|
|
10
|
+
return 'show-coverage';
|
|
11
|
+
}
|
|
12
|
+
get description() {
|
|
13
|
+
return `Open the tests coverage report.`;
|
|
14
|
+
}
|
|
15
|
+
get requiredDependencies() {
|
|
16
|
+
return ['nyc'];
|
|
17
|
+
}
|
|
18
|
+
async configure(command) {
|
|
19
|
+
command.option(`--report <path>`, `The relative path to the coverage report directory.`, {
|
|
20
|
+
default: `output/coverage`,
|
|
21
|
+
validator: core_1.program.STRING,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async main() {
|
|
25
|
+
if (configs_1.configs.isWindows) {
|
|
26
|
+
await this.invokeShellCommand('start', ['', this.getReportDir()], {
|
|
27
|
+
useShellOption: true,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
await this.invokeShellCommand('open', [this.getReportDir()]);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
getReportDir() {
|
|
35
|
+
const reportDir = path.resolve(configs_1.configs.libRoot, this.options.report, 'lcov-report/index.html');
|
|
36
|
+
return reportDir;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.ShowCoverageScript = ShowCoverageScript;
|
|
40
|
+
//# sourceMappingURL=showCoverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"showCoverage.js","sourceRoot":"","sources":["../../scripts/showCoverage.ts"],"names":[],"mappings":";;;AAAA,wCAAiD;AACjD,6DAAiE;AACjE,6BAA6B;AAC7B,mDAAgD;AAMhD,MAAa,kBAAmB,SAAQ,gBAAmB;IACzD,IAAI,IAAI;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,iCAAiC,CAAC;IAC3C,CAAC;IAED,IAAc,oBAAoB;QAChC,OAAO,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IAES,KAAK,CAAC,SAAS,CAAC,OAAgB;QACxC,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,qDAAqD,EAAE;YACvF,OAAO,EAAE,iBAAiB;YAC1B,SAAS,EAAE,cAAO,CAAC,MAAM;SAC1B,CAAC,CAAC;IACL,CAAC;IAES,KAAK,CAAC,IAAI;QAClB,IAAI,iBAAO,CAAC,SAAS,EAAE;YACrB,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE;gBAChE,cAAc,EAAE,IAAI;aACrB,CAAC,CAAC;SACJ;aAAM;YACL,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;SAC9D;IACH,CAAC;IAES,YAAY;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAO,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;QAC/F,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAlCD,gDAkCC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@caporal/core';
|
|
2
|
+
import { ScriptBase } from '@villedemontreal/scripting/dist/src';
|
|
3
|
+
export interface Options {
|
|
4
|
+
bail?: boolean;
|
|
5
|
+
jenkins?: boolean;
|
|
6
|
+
report?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class TestScript extends ScriptBase<Options> {
|
|
9
|
+
get name(): string;
|
|
10
|
+
get description(): string;
|
|
11
|
+
protected configure(command: Command): Promise<void>;
|
|
12
|
+
protected main(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TestScript = void 0;
|
|
4
|
+
const core_1 = require("@caporal/core");
|
|
5
|
+
const src_1 = require("@villedemontreal/scripting/dist/src");
|
|
6
|
+
const lint_1 = require("./lint");
|
|
7
|
+
const testUnits_1 = require("./testUnits");
|
|
8
|
+
class TestScript extends src_1.ScriptBase {
|
|
9
|
+
get name() {
|
|
10
|
+
return 'test';
|
|
11
|
+
}
|
|
12
|
+
get description() {
|
|
13
|
+
return `Run the unit tests + the linting validations.`;
|
|
14
|
+
}
|
|
15
|
+
async configure(command) {
|
|
16
|
+
command.option(`--bail`, `Stop the execution of the tests as soon as an error occures.`);
|
|
17
|
+
command.option(`--jenkins`, `Configure the tests to be run by Jenkins.`);
|
|
18
|
+
command.option(`--report <path>`, `The relative path to the report, when the tests are run for Jenkins.`, {
|
|
19
|
+
default: `output/test-results/report.xml`,
|
|
20
|
+
validator: core_1.program.STRING,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async main() {
|
|
24
|
+
await this.invokeScript(lint_1.LintScript, {}, {});
|
|
25
|
+
await this.invokeScript(testUnits_1.TestUnitsScript, this.options, {});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.TestScript = TestScript;
|
|
29
|
+
//# sourceMappingURL=test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.js","sourceRoot":"","sources":["../../scripts/test.ts"],"names":[],"mappings":";;;AAAA,wCAAiD;AACjD,6DAAiE;AACjE,iCAAoC;AACpC,2CAA8C;AAQ9C,MAAa,UAAW,SAAQ,gBAAmB;IACjD,IAAI,IAAI;QACN,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,+CAA+C,CAAC;IACzD,CAAC;IAES,KAAK,CAAC,SAAS,CAAC,OAAgB;QACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,8DAA8D,CAAC,CAAC;QACzF,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,2CAA2C,CAAC,CAAC;QACzE,OAAO,CAAC,MAAM,CACZ,iBAAiB,EACjB,sEAAsE,EACtE;YACE,OAAO,EAAE,gCAAgC;YACzC,SAAS,EAAE,cAAO,CAAC,MAAM;SAC1B,CACF,CAAC;IACJ,CAAC;IAES,KAAK,CAAC,IAAI;QAClB,MAAM,IAAI,CAAC,YAAY,CAAC,iBAAU,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5C,MAAM,IAAI,CAAC,YAAY,CAAC,2BAAe,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;CACF;AA1BD,gCA0BC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from '@caporal/core';
|
|
2
|
+
import { ScriptBase } from '@villedemontreal/scripting/dist/src';
|
|
3
|
+
export interface Options {
|
|
4
|
+
bail?: boolean;
|
|
5
|
+
jenkins?: boolean;
|
|
6
|
+
report?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class TestUnitsScript extends ScriptBase<Options> {
|
|
9
|
+
get name(): string;
|
|
10
|
+
get description(): string;
|
|
11
|
+
protected configure(command: Command): Promise<void>;
|
|
12
|
+
protected get requiredDependencies(): string[];
|
|
13
|
+
private addQuotes;
|
|
14
|
+
protected main(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TestUnitsScript = void 0;
|
|
4
|
+
const core_1 = require("@caporal/core");
|
|
5
|
+
const src_1 = require("@villedemontreal/scripting/dist/src");
|
|
6
|
+
const _ = require("lodash");
|
|
7
|
+
const configs_1 = require("../src/config/configs");
|
|
8
|
+
const TESTS_LOCATIONS = [`${configs_1.configs.libRoot}/dist/src/**/*.test.js`];
|
|
9
|
+
class TestUnitsScript extends src_1.ScriptBase {
|
|
10
|
+
get name() {
|
|
11
|
+
return 'test-units';
|
|
12
|
+
}
|
|
13
|
+
get description() {
|
|
14
|
+
return `Run the unit tests.`;
|
|
15
|
+
}
|
|
16
|
+
async configure(command) {
|
|
17
|
+
command.option(`--bail`, `Stop the execution of the tests as soon as an error occures.`);
|
|
18
|
+
command.option(`--jenkins`, `Configure the tests to be run by Jenkins.`);
|
|
19
|
+
command.option(`--report <path>`, `The relative path to the report, when the tests are run for Jenkins.`, {
|
|
20
|
+
default: `output/test-results/report.xml`,
|
|
21
|
+
validator: core_1.program.STRING,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
get requiredDependencies() {
|
|
25
|
+
const deps = ['mocha'];
|
|
26
|
+
if (this.options.jenkins) {
|
|
27
|
+
deps.push('mocha-jenkins-reporter');
|
|
28
|
+
}
|
|
29
|
+
return deps;
|
|
30
|
+
}
|
|
31
|
+
addQuotes(tokens) {
|
|
32
|
+
if (_.isNil(tokens) || tokens.length === 0) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
return tokens.map((token) => {
|
|
36
|
+
return _.isNil(token) ? token : `"${_.trim(token, '"')}"`;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async main() {
|
|
40
|
+
const cmdArgs = [];
|
|
41
|
+
if (await this.isProjectDirectDependency(`nyc`)) {
|
|
42
|
+
cmdArgs.push(`${configs_1.configs.libRoot}/node_modules/nyc/bin/nyc`);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.logger.warn(`The "nyc" direct dependency was not found in your project. The tests will be run using Mocha only!`);
|
|
46
|
+
}
|
|
47
|
+
cmdArgs.push(`${configs_1.configs.libRoot}/node_modules/mocha/bin/_mocha`);
|
|
48
|
+
// ==========================================
|
|
49
|
+
// The test locations need to be quoted because
|
|
50
|
+
// they may contain a "**" wildcard that some
|
|
51
|
+
// shells may interpret differently otherwise!
|
|
52
|
+
//
|
|
53
|
+
// @see https://mochajs.org/#the-test-directory
|
|
54
|
+
// ==========================================
|
|
55
|
+
cmdArgs.push(...this.addQuotes(TESTS_LOCATIONS));
|
|
56
|
+
cmdArgs.push(`--exit`);
|
|
57
|
+
// ==========================================
|
|
58
|
+
// Stop testing as soon as one test fails?
|
|
59
|
+
// ==========================================
|
|
60
|
+
if (this.options.bail) {
|
|
61
|
+
cmdArgs.push('--bail');
|
|
62
|
+
}
|
|
63
|
+
// ==========================================
|
|
64
|
+
// For Jenkins, the path to the report to generate
|
|
65
|
+
// can be passed :
|
|
66
|
+
// - as a command line param :
|
|
67
|
+
// "run test-units --jenkins --report output/test-results/report.xml"
|
|
68
|
+
// - as an "JUNIT_REPORT_PATH" environment variable.
|
|
69
|
+
//
|
|
70
|
+
// By default, the path will be "output/test-results/report.xml"
|
|
71
|
+
// ==========================================
|
|
72
|
+
if (this.options.jenkins) {
|
|
73
|
+
if (this.options.report) {
|
|
74
|
+
process.env.JUNIT_REPORT_PATH = this.options.report;
|
|
75
|
+
}
|
|
76
|
+
else if (!process.env.JUNIT_REPORT_PATH) {
|
|
77
|
+
process.env.JUNIT_REPORT_PATH = 'output/test-results/report.xml';
|
|
78
|
+
}
|
|
79
|
+
this.logger.info('Exporting tests to junit file ' + process.env.JUNIT_REPORT_PATH);
|
|
80
|
+
cmdArgs.push('--reporter');
|
|
81
|
+
cmdArgs.push('mocha-jenkins-reporter');
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
await this.invokeShellCommand('node', cmdArgs, {
|
|
85
|
+
useTestsNodeAppInstance: true,
|
|
86
|
+
});
|
|
87
|
+
this.logger.info(" \u21b3 type 'run show-coverage' (or './run show-coverage' on Linux/Mac) to display the HTML report");
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
throw new Error('Some unit tests failed');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.TestUnitsScript = TestUnitsScript;
|
|
95
|
+
//# sourceMappingURL=testUnits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testUnits.js","sourceRoot":"","sources":["../../scripts/testUnits.ts"],"names":[],"mappings":"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"}
|