@anmiles/google-api-wrapper 9.0.0 → 9.1.0

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/.eslintrc.js CHANGED
@@ -10,4 +10,7 @@ module.exports = {
10
10
  'input/',
11
11
  'secrets/',
12
12
  ],
13
+ globals : {
14
+ NodeJS : true,
15
+ },
13
16
  };
package/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [9.1.0](../../tags/v9.1.0) - 2023-05-26
9
+ ### Changed
10
+ - Concurrent servers on the same port between different applications
11
+ - Use `event-emitter` to mock subscriptions on server/response
12
+ - Get rid of timeouts and promise races in tests
13
+
8
14
  ## [9.0.0](../../tags/v9.0.0) - 2023-05-15
9
15
  ### Changed
10
16
  - Update `@anmiles/logger` with breaking change (removing timestamps for colored logs)
@@ -13,10 +13,12 @@ require("@anmiles/prototypes");
13
13
  const paths_1 = require("./paths");
14
14
  const secrets_1 = __importDefault(require("./secrets"));
15
15
  exports.default = { getScopes, getSecrets, getCredentials, validateCredentials, createCredentials, checkSecrets, getSecretsError, getScopesError };
16
- const callbackPort = 6006;
17
- const startURI = `http://localhost:${callbackPort}/`;
18
- const callbackURI = `http://localhost:${callbackPort}/oauthcallback`;
16
+ const port = 6006;
17
+ const host = `localhost:${port}`;
18
+ const startURI = `http://${host}/`;
19
+ const callbackURI = `http://${host}/oauthcallback`;
19
20
  const tokenExpiration = 7 * 24 * 60 * 60 * 1000;
21
+ const serverRetryInterval = 1000;
20
22
  function getScopes() {
21
23
  const scopesFile = (0, paths_1.getScopesFile)();
22
24
  const scopes = fs_1.default.getJSON(scopesFile, () => {
@@ -70,7 +72,9 @@ async function createCredentials(profile, auth, options, prompt) {
70
72
  prompt,
71
73
  scope,
72
74
  });
73
- const server = http_1.default.createServer(async (request, response) => {
75
+ const server = http_1.default.createServer();
76
+ (0, server_destroy_1.default)(server);
77
+ server.on('request', async (request, response) => {
74
78
  if (!request.url) {
75
79
  response.end('');
76
80
  return;
@@ -87,10 +91,19 @@ async function createCredentials(profile, auth, options, prompt) {
87
91
  const { tokens } = await auth.getToken(code);
88
92
  resolve(tokens);
89
93
  });
90
- (0, server_destroy_1.default)(server);
91
- server.listen(callbackPort);
92
- (0, logger_1.warn)('Please check your browser for further actions');
93
- (0, open_1.default)(startURI);
94
+ server.on('error', (error) => {
95
+ if (error.code === 'EADDRINUSE') {
96
+ setTimeout(() => server.listen(port), serverRetryInterval);
97
+ }
98
+ else {
99
+ throw error;
100
+ }
101
+ });
102
+ server.once('listening', () => {
103
+ (0, logger_1.warn)('Please check your browser for further actions');
104
+ (0, open_1.default)(startURI);
105
+ });
106
+ server.listen(port);
94
107
  });
95
108
  }
96
109
  function formatMessage(message) {
@@ -1 +1 @@
1
- {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anmiles/google-api-wrapper",
3
- "version": "9.0.0",
3
+ "version": "9.1.0",
4
4
  "description": "Provides quick interface for getting google API data",
5
5
  "keywords": [
6
6
  "google",
@@ -39,6 +39,7 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@anmiles/eslint-config": "^1.0.6",
42
+ "@types/event-emitter": "^0.3.3",
42
43
  "@types/jest": "^29.5.1",
43
44
  "@types/server-destroy": "^1.0.1",
44
45
  "@typescript-eslint/eslint-plugin": "^5.59.2",
@@ -47,6 +48,7 @@
47
48
  "eslint-plugin-align-assignments": "^1.1.2",
48
49
  "eslint-plugin-import": "^2.27.5",
49
50
  "eslint-plugin-jest": "^27.2.1",
51
+ "event-emitter": "^0.3.5",
50
52
  "jest": "^29.5.0",
51
53
  "nyc": "^15.1.0",
52
54
  "rimraf": "^5.0.0",
@@ -4,6 +4,7 @@ import path from 'path';
4
4
  import open from 'open';
5
5
  import type GoogleApis from 'googleapis';
6
6
  import logger from '@anmiles/logger';
7
+ import emitter from 'event-emitter';
7
8
  import paths from '../paths';
8
9
  import type { Secrets } from '../../types';
9
10
  import '@anmiles/prototypes';
@@ -22,18 +23,21 @@ jest.mock<typeof secrets>('../secrets', () => ({
22
23
  }));
23
24
 
24
25
  jest.mock<Partial<typeof http>>('http', () => ({
25
- createServer : jest.fn().mockImplementation((callback) => {
26
- serverCallback = callback;
27
-
28
- return {
29
- on,
30
- listen,
31
- close,
32
- destroy,
33
- };
34
- }),
26
+ createServer : jest.fn().mockImplementation(() => server),
35
27
  }));
36
28
 
29
+ let server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
30
+ let response: http.ServerResponse;
31
+
32
+ async function makeRequest(url: string | undefined) {
33
+ server.emit('request', {
34
+ url,
35
+ headers : {
36
+ host,
37
+ },
38
+ } as http.IncomingMessage, response);
39
+ }
40
+
37
41
  jest.mock<Partial<typeof fs>>('fs', () => ({
38
42
  existsSync : jest.fn().mockImplementation(() => exists),
39
43
  }));
@@ -43,7 +47,7 @@ jest.mock<Partial<typeof path>>('path', () => ({
43
47
  }));
44
48
 
45
49
  jest.mock('open', () => jest.fn().mockImplementation((url: string) => {
46
- willOpen(url.replace('http://localhost:6006', ''));
50
+ makeRequest(url.replace('http://localhost:6006', ''));
47
51
  }));
48
52
 
49
53
  jest.mock<Partial<typeof logger>>('@anmiles/logger', () => ({
@@ -56,6 +60,12 @@ jest.mock<Partial<typeof paths>>('../paths', () => ({
56
60
  getCredentialsFile : jest.fn().mockImplementation(() => credentialsFile),
57
61
  }));
58
62
 
63
+ jest.useFakeTimers();
64
+
65
+ const port = 6006;
66
+ const host = `localhost:${port}`;
67
+ const callbackURI = `http://${host}/oauthcallback`;
68
+
59
69
  const profile = 'username1';
60
70
  const scopesFile = 'scopes.json';
61
71
  const secretsFile = 'secrets/username1.json';
@@ -79,7 +89,7 @@ const secretsJSON: Secrets = {
79
89
  token_uri : 'https://oauth2.googleapis.com/token',
80
90
  auth_provider_x509_cert_url : 'https://www.googleapis.com/oauth2/v1/certs',
81
91
  client_secret : 'client_secret',
82
- redirect_uris : [ 'http://localhost:6006/oauthcallback' ],
92
+ redirect_uris : [ callbackURI ],
83
93
  /* eslint-enable camelcase */
84
94
  },
85
95
  };
@@ -98,46 +108,6 @@ const auth = {
98
108
  getToken : jest.fn().mockResolvedValue({ tokens : credentialsJSON }),
99
109
  } as unknown as GoogleApis.Common.OAuth2Client;
100
110
 
101
- const response = {
102
- end : jest.fn(),
103
- } as unknown as http.ServerResponse;
104
-
105
- let serverCallback: (
106
- request: http.IncomingMessage,
107
- response: http.ServerResponse
108
- ) => Promise<typeof credentialsJSON>;
109
-
110
- function willOpen(url: string | undefined, timeout?: number) {
111
- setTimeout(async () => {
112
- await serverCallback({
113
- url,
114
- headers : {
115
- host : 'localhost:6006',
116
- },
117
- } as http.IncomingMessage, response);
118
- }, timeout || 0);
119
- }
120
- let closedTime: number;
121
-
122
- const on = jest.fn().mockImplementation((event: string, listener: (...args: any[]) => void) => {
123
- if (event === 'connection') {
124
- // always simulate opening several connections once connections are meant to be listened
125
- connections.forEach((connection) => listener(connection));
126
- }
127
- });
128
-
129
- const listen = jest.fn();
130
- const close = jest.fn().mockImplementation(() => {
131
- closedTime = new Date().getTime();
132
- });
133
- const destroy = jest.fn();
134
-
135
- const connections = [
136
- { remoteAddress : 'server', remotePort : '1001', on : jest.fn(), destroy : jest.fn() },
137
- { remoteAddress : 'server', remotePort : '1002', on : jest.fn(), destroy : jest.fn() },
138
- { remoteAddress : 'server', remotePort : '1003', on : jest.fn(), destroy : jest.fn() },
139
- ];
140
-
141
111
  let exists: boolean;
142
112
 
143
113
  let getJSONSpy: jest.SpyInstance;
@@ -360,10 +330,38 @@ describe('src/lib/secrets', () => {
360
330
  describe('createCredentials', () => {
361
331
  const tokenUrl = `/request.url?code=${code}`;
362
332
 
363
- it('should generate authUrl', async () => {
364
- willOpen(tokenUrl, 100);
333
+ const connections = [
334
+ { remoteAddress : 'server', remotePort : '1001', on : jest.fn(), destroy : jest.fn() },
335
+ { remoteAddress : 'server', remotePort : '1002', on : jest.fn(), destroy : jest.fn() },
336
+ { remoteAddress : 'server', remotePort : '1003', on : jest.fn(), destroy : jest.fn() },
337
+ ];
338
+
339
+ let endSpy: jest.SpyInstance;
340
+
341
+ beforeEach(() => {
342
+ server = emitter({
343
+ listen : jest.fn().mockImplementation(() => {
344
+ // always simulate opening several connections once connections are meant to be listened
345
+ connections.forEach((connection) => server.emit('connection', connection));
346
+ }),
347
+ close : jest.fn(),
348
+ destroy : jest.fn(),
349
+ }) as typeof server;
350
+
351
+ response = emitter({
352
+ end : jest.fn(),
353
+ }) as typeof response;
354
+
355
+ endSpy = jest.spyOn(response, 'end');
356
+ });
365
357
 
366
- await original.createCredentials(profile, auth);
358
+ afterAll(() => {
359
+ endSpy.mockRestore();
360
+ });
361
+
362
+ it('should generate authUrl', async () => {
363
+ original.createCredentials(profile, auth);
364
+ await Promise.resolve();
367
365
 
368
366
  expect(auth.generateAuthUrl).toHaveBeenCalledWith({
369
367
  // eslint-disable-next-line camelcase
@@ -377,9 +375,8 @@ describe('src/lib/secrets', () => {
377
375
  });
378
376
 
379
377
  it('should generate authUrl and require consent if explicitly asked', async () => {
380
- willOpen(tokenUrl, 100);
381
-
382
- await original.createCredentials(profile, auth, { temporary : true }, 'consent');
378
+ original.createCredentials(profile, auth, { temporary : true }, 'consent');
379
+ await Promise.resolve();
383
380
 
384
381
  expect(auth.generateAuthUrl).toHaveBeenCalledWith({
385
382
  // eslint-disable-next-line camelcase
@@ -393,9 +390,8 @@ describe('src/lib/secrets', () => {
393
390
  });
394
391
 
395
392
  it('should generate authUrl with custom scopes', async () => {
396
- willOpen(tokenUrl, 100);
397
-
398
- await original.createCredentials(profile, auth, { scopes : [ 'scope1', 'scope2' ] });
393
+ original.createCredentials(profile, auth, { scopes : [ 'scope1', 'scope2' ] });
394
+ await Promise.resolve();
399
395
 
400
396
  expect(auth.generateAuthUrl).toHaveBeenCalledWith({
401
397
  // eslint-disable-next-line camelcase
@@ -406,39 +402,45 @@ describe('src/lib/secrets', () => {
406
402
  });
407
403
 
408
404
  it('should create server on 6006 port', async () => {
409
- willOpen(tokenUrl, 100);
410
-
411
- await original.createCredentials(profile, auth);
405
+ original.createCredentials(profile, auth);
406
+ await Promise.resolve();
412
407
 
413
408
  expect(http.createServer).toHaveBeenCalled();
414
- expect(listen).toHaveBeenCalledWith(6006);
409
+ expect(server.listen).toHaveBeenCalledWith(6006);
415
410
  });
416
411
 
417
- it('should open browser page and warn about it', async () => {
418
- willOpen(tokenUrl, 100);
412
+ it('should open browser page and warn about it once listening', async () => {
413
+ original.createCredentials(profile, auth);
414
+ await Promise.resolve();
419
415
 
420
- await original.createCredentials(profile, auth);
416
+ server.emit('listening');
421
417
 
422
418
  expect(open).toHaveBeenCalledWith('http://localhost:6006/');
423
419
  expect(logger.warn).toHaveBeenCalledWith('Please check your browser for further actions');
424
420
  });
425
421
 
426
- it('should show nothing on the browser page if request.url is empty', async () => {
427
- willOpen('', 100);
428
- willOpen(tokenUrl, 200);
422
+ it('should not open browser page and warn about it until listening', async () => {
423
+ original.createCredentials(profile, auth);
424
+ await Promise.resolve();
429
425
 
430
- await original.createCredentials(profile, auth);
426
+ expect(open).not.toHaveBeenCalled();
427
+ expect(logger.warn).not.toHaveBeenCalled();
428
+ });
429
+
430
+ it('should show nothing on the browser page if request.url is empty', async () => {
431
+ original.createCredentials(profile, auth);
432
+ makeRequest('');
433
+ await Promise.resolve();
431
434
 
432
- expect(response.end).toHaveBeenCalledWith('');
435
+ expect(endSpy).toHaveBeenCalledWith('');
433
436
  });
434
437
 
435
438
  it('should show opening instructions if opened the home page', async () => {
436
- willOpen('/', 100);
437
- willOpen(tokenUrl, 200);
439
+ original.createCredentials(profile, auth);
440
+ makeRequest('/');
441
+ await Promise.resolve();
438
442
 
439
- await original.createCredentials(profile, auth);
440
-
441
- expect(response.end).toHaveBeenCalledWith(`\
443
+ expect(endSpy).toHaveBeenCalledWith(`\
442
444
  <div style="width: 100%;height: 100%;display: flex;align-items: start;justify-content: center">\n\
443
445
  <div style="padding: 0 1em;border: 1px solid black;font-family: Arial, sans-serif;margin: 1em;">\n\
444
446
  <p>Please open <a href="${authUrl}">auth page</a> using <strong>${profile}</strong> google profile</p>\n\
@@ -449,11 +451,11 @@ describe('src/lib/secrets', () => {
449
451
  });
450
452
 
451
453
  it('should ask to close webpage', async () => {
452
- willOpen(tokenUrl, 100);
453
-
454
- await original.createCredentials(profile, auth);
454
+ original.createCredentials(profile, auth);
455
+ makeRequest(tokenUrl);
456
+ await Promise.resolve();
455
457
 
456
- expect(response.end).toHaveBeenCalledWith('\
458
+ expect(endSpy).toHaveBeenCalledWith('\
457
459
  <div style="width: 100%;height: 100%;display: flex;align-items: start;justify-content: center">\n\
458
460
  <div style="padding: 0 1em;border: 1px solid black;font-family: Arial, sans-serif;margin: 1em;">\n\
459
461
  <p>Please close this page and return to application</p>\n\
@@ -462,53 +464,53 @@ describe('src/lib/secrets', () => {
462
464
  });
463
465
 
464
466
  it('should close server and destroy all connections if request.url is truthy', async () => {
465
- willOpen(tokenUrl, 100);
466
-
467
- await original.createCredentials(profile, auth);
467
+ original.createCredentials(profile, auth);
468
+ makeRequest(tokenUrl);
469
+ await Promise.resolve();
468
470
 
469
- expect(close).toHaveBeenCalled();
471
+ expect(server.close).toHaveBeenCalled();
470
472
 
471
473
  connections.forEach((connection) => expect(connection.destroy).toHaveBeenCalled());
472
474
  });
473
475
 
474
- it('should only resolve when request.url is truthy', async () => {
475
- const emptyRequestTime = 100;
476
- const requestTime = 200;
477
-
478
- const before = new Date().getTime();
479
- willOpen(undefined, emptyRequestTime);
480
- willOpen(tokenUrl, requestTime);
476
+ it('should close server and resolve if request.url is truthy', async () => {
477
+ const promise = original.createCredentials(profile, auth);
478
+ makeRequest(tokenUrl);
479
+ const result = await Promise.resolve(promise);
480
+ expect(result).toEqual(credentialsJSON);
481
+ expect(server.close).toHaveBeenCalledTimes(1);
482
+ });
481
483
 
482
- const result = await original.createCredentials(profile, auth);
483
- const after = new Date().getTime();
484
+ it('should not close server if request.url is falsy', async () => {
485
+ original.createCredentials(profile, auth);
486
+ makeRequest(undefined);
487
+ await Promise.resolve();
484
488
 
485
- expect(close).toHaveBeenCalledTimes(1);
486
- expect(closedTime - before).toBeGreaterThanOrEqual(requestTime - 1);
487
- expect(after - before).toBeGreaterThanOrEqual(requestTime - 1);
488
- expect(result).toEqual(credentialsJSON);
489
+ expect(server.close).not.toHaveBeenCalled();
489
490
  });
490
491
 
491
- it('should only resolve when request.url contains no code', async () => {
492
- const noCodeRequestTime = 100;
493
- const requestTime = 200;
492
+ it('should re-throw a server error if error is not EADDRINUSE', () => {
493
+ const error = { code : 'RANDOM', message : 'random error' } as NodeJS.ErrnoException;
494
494
 
495
- const before = new Date().getTime();
496
- willOpen('/request.url?param=value', noCodeRequestTime);
497
- willOpen(tokenUrl, requestTime);
495
+ original.createCredentials(profile, auth);
496
+ expect(() => server.emit('error', error)).toThrow(error.message);
497
+ });
498
498
 
499
- const result = await original.createCredentials(profile, auth);
500
- const after = new Date().getTime();
499
+ it('should not re-throw a server error and try to listen again in 1000 seconds if error is EADDRINUSE', () => {
500
+ const error = { code : 'EADDRINUSE' } as NodeJS.ErrnoException;
501
501
 
502
- expect(close).toHaveBeenCalledTimes(1);
503
- expect(closedTime - before).toBeGreaterThanOrEqual(requestTime - 1);
504
- expect(after - before).toBeGreaterThanOrEqual(requestTime - 1);
505
- expect(result).toEqual(credentialsJSON);
502
+ original.createCredentials(profile, auth);
503
+ expect(server.listen).toHaveBeenCalledTimes(1);
504
+ expect(() => server.emit('error', error)).not.toThrow();
505
+ expect(server.listen).toHaveBeenCalledTimes(1);
506
+ jest.advanceTimersByTime(1000);
507
+ expect(server.listen).toHaveBeenCalledTimes(2);
506
508
  });
507
509
 
508
510
  it('should return credentials JSON', async () => {
509
- willOpen(tokenUrl, 100);
510
-
511
- const result = await original.createCredentials(profile, auth);
511
+ const promise = original.createCredentials(profile, auth);
512
+ makeRequest(tokenUrl);
513
+ const result = await promise;
512
514
 
513
515
  expect(result).toEqual(credentialsJSON);
514
516
  });
@@ -13,10 +13,12 @@ import secrets from './secrets';
13
13
  export { getSecrets, getCredentials };
14
14
  export default { getScopes, getSecrets, getCredentials, validateCredentials, createCredentials, checkSecrets, getSecretsError, getScopesError };
15
15
 
16
- const callbackPort = 6006;
17
- const startURI = `http://localhost:${callbackPort}/`;
18
- const callbackURI = `http://localhost:${callbackPort}/oauthcallback`;
19
- const tokenExpiration = 7 * 24 * 60 * 60 * 1000;
16
+ const port = 6006;
17
+ const host = `localhost:${port}`;
18
+ const startURI = `http://${host}/`;
19
+ const callbackURI = `http://${host}/oauthcallback`;
20
+ const tokenExpiration = 7 * 24 * 60 * 60 * 1000;
21
+ const serverRetryInterval = 1000;
20
22
 
21
23
  function getScopes(): string[] {
22
24
  const scopesFile = getScopesFile();
@@ -81,7 +83,10 @@ async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Cl
81
83
  scope,
82
84
  });
83
85
 
84
- const server = http.createServer(async (request, response) => {
86
+ const server = http.createServer();
87
+ enableDestroy(server);
88
+
89
+ server.on('request', async (request, response) => {
85
90
  if (!request.url) {
86
91
  response.end('');
87
92
  return;
@@ -102,10 +107,20 @@ async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Cl
102
107
  resolve(tokens);
103
108
  });
104
109
 
105
- enableDestroy(server);
106
- server.listen(callbackPort);
107
- warn('Please check your browser for further actions');
108
- open(startURI);
110
+ server.on('error', (error: NodeJS.ErrnoException) => {
111
+ if (error.code === 'EADDRINUSE') {
112
+ setTimeout(() => server.listen(port), serverRetryInterval);
113
+ } else {
114
+ throw error;
115
+ }
116
+ });
117
+
118
+ server.once('listening', () => {
119
+ warn('Please check your browser for further actions');
120
+ open(startURI);
121
+ });
122
+
123
+ server.listen(port);
109
124
  });
110
125
  }
111
126