@anmiles/google-api-wrapper 17.0.8 → 18.0.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.
Files changed (68) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.js +23 -7
  3. package/.vscode/settings.json +1 -0
  4. package/CHANGELOG.md +10 -0
  5. package/README.md +4 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/api.d.ts +16 -16
  10. package/dist/lib/api.d.ts.map +1 -1
  11. package/dist/lib/api.js +9 -7
  12. package/dist/lib/api.js.map +1 -1
  13. package/dist/lib/auth.d.ts +3 -3
  14. package/dist/lib/auth.d.ts.map +1 -1
  15. package/dist/lib/auth.js +1 -1
  16. package/dist/lib/auth.js.map +1 -1
  17. package/dist/lib/paths.d.ts +5 -5
  18. package/dist/lib/paths.d.ts.map +1 -1
  19. package/dist/lib/paths.js +1 -1
  20. package/dist/lib/paths.js.map +1 -1
  21. package/dist/lib/profiles.d.ts +4 -4
  22. package/dist/lib/profiles.d.ts.map +1 -1
  23. package/dist/lib/profiles.js +4 -4
  24. package/dist/lib/profiles.js.map +1 -1
  25. package/dist/lib/renderer.d.ts +2 -2
  26. package/dist/lib/renderer.d.ts.map +1 -1
  27. package/dist/lib/renderer.js +1 -1
  28. package/dist/lib/renderer.js.map +1 -1
  29. package/dist/lib/secrets.d.ts +14 -13
  30. package/dist/lib/secrets.d.ts.map +1 -1
  31. package/dist/lib/secrets.js +13 -12
  32. package/dist/lib/secrets.js.map +1 -1
  33. package/dist/types/options.d.ts +9 -0
  34. package/dist/types/options.d.ts.map +1 -0
  35. package/dist/types/{common.js → options.js} +1 -1
  36. package/dist/types/options.js.map +1 -0
  37. package/dist/types/secrets.d.ts +2 -5
  38. package/dist/types/secrets.d.ts.map +1 -1
  39. package/jest.config.js +3 -11
  40. package/package.json +63 -55
  41. package/src/index.ts +2 -0
  42. package/src/lib/__tests__/api.test.ts +30 -24
  43. package/src/lib/__tests__/auth.test.ts +4 -5
  44. package/src/lib/__tests__/paths.test.ts +6 -5
  45. package/src/lib/__tests__/profiles.test.ts +17 -27
  46. package/src/lib/__tests__/renderer.test.ts +7 -7
  47. package/src/lib/__tests__/secrets.test.ts +54 -77
  48. package/src/lib/api.ts +36 -26
  49. package/src/lib/auth.ts +5 -5
  50. package/src/lib/paths.ts +8 -8
  51. package/src/lib/profiles.ts +6 -6
  52. package/src/lib/renderer.ts +8 -9
  53. package/src/lib/secrets.ts +22 -19
  54. package/src/types/options.ts +10 -0
  55. package/src/types/secrets.ts +9 -13
  56. package/tsconfig.build.json +2 -2
  57. package/tsconfig.json +5 -5
  58. package/tsconfig.test.json +3 -3
  59. package/.gitlab-ci.yml +0 -117
  60. package/dist/types/common.d.ts +0 -4
  61. package/dist/types/common.d.ts.map +0 -1
  62. package/dist/types/common.js.map +0 -1
  63. package/dist/types/index.d.ts +0 -3
  64. package/dist/types/index.d.ts.map +0 -1
  65. package/dist/types/index.js +0 -19
  66. package/dist/types/index.js.map +0 -1
  67. package/src/types/common.ts +0 -3
  68. package/src/types/index.ts +0 -2
@@ -1,17 +1,18 @@
1
1
  import fs from 'fs';
2
2
  import http from 'http';
3
- import path from 'path';
3
+ import type path from 'path';
4
4
  import { open } from 'out-url';
5
5
  import type GoogleApis from 'googleapis';
6
6
  import logger from '@anmiles/logger';
7
7
  import emitter from 'event-emitter';
8
- import renderer from '../renderer';
9
- import paths from '../paths';
10
- import type { Secrets } from '../../types';
8
+ import type renderer from '../renderer';
9
+ import type paths from '../paths';
10
+ import type { Secrets } from '../../types/secrets';
11
11
  import '@anmiles/prototypes';
12
12
 
13
13
  import secrets from '../secrets';
14
- const original = jest.requireActual('../secrets').default as typeof secrets;
14
+
15
+ const original = jest.requireActual<{ default : typeof secrets }>('../secrets').default;
15
16
  jest.mock<typeof secrets>('../secrets', () => ({
16
17
  getScopes : jest.fn().mockImplementation(() => scopesJSON),
17
18
  getSecrets : jest.fn().mockImplementation(() => secretsJSON),
@@ -25,7 +26,7 @@ jest.mock<typeof secrets>('../secrets', () => ({
25
26
  }));
26
27
 
27
28
  jest.mock<Partial<typeof renderer>>('../renderer', () => ({
28
- renderAuth : jest.fn().mockImplementation(({ profile, authUrl, scope } : { profile: string, authUrl: string, scope: string[] }) => `content = profile = ${profile} authUrl = ${authUrl} scope = ${scope.join('|')}`),
29
+ renderAuth : jest.fn().mockImplementation(({ profile, authUrl, scope } : { profile : string; authUrl : string; scope : string[] }) => `content = profile = ${profile} authUrl = ${authUrl} scope = ${scope.join('|')}`),
29
30
  renderDone : jest.fn().mockImplementation(() => 'content = done'),
30
31
  }));
31
32
 
@@ -33,10 +34,10 @@ jest.mock<Partial<typeof http>>('http', () => ({
33
34
  createServer : jest.fn().mockImplementation(() => server),
34
35
  }));
35
36
 
36
- let server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
37
+ let server: http.Server;
37
38
  let response: http.ServerResponse;
38
39
 
39
- async function makeRequest(url: string | undefined) {
40
+ function makeRequest(url: string | undefined): void {
40
41
  server.emit('request', {
41
42
  url,
42
43
  headers : {
@@ -51,11 +52,13 @@ jest.mock<Partial<typeof fs>>('fs', () => ({
51
52
  }));
52
53
 
53
54
  jest.mock<Partial<typeof path>>('path', () => ({
54
- join : jest.fn().mockImplementation((...args) => args.join('/')),
55
+ join : jest.fn().mockImplementation((...paths: string[]) => paths.join('/')),
55
56
  }));
56
57
 
57
- jest.mock<{ open: typeof open }>('out-url', () => ({
58
- open : jest.fn().mockImplementation(async (url) => makeRequest(url.replace('http://localhost:6006', ''))),
58
+ jest.mock<{ open : typeof open }>('out-url', () => ({
59
+ open : jest.fn().mockImplementation((url: string) => {
60
+ makeRequest(url.replace('http://localhost:6006', ''));
61
+ }),
59
62
  }));
60
63
 
61
64
  jest.mock<Partial<typeof logger>>('@anmiles/logger', () => ({
@@ -92,7 +95,6 @@ const scopesJSON: string[] = [
92
95
 
93
96
  const secretsJSON: Secrets = {
94
97
  web : {
95
- /* eslint-disable camelcase */
96
98
  client_id : 'client_id.apps.googleusercontent.com',
97
99
  project_id : 'project_id',
98
100
  auth_uri : 'https://accounts.google.com/o/oauth2/auth',
@@ -100,16 +102,14 @@ const secretsJSON: Secrets = {
100
102
  auth_provider_x509_cert_url : 'https://www.googleapis.com/oauth2/v1/certs',
101
103
  client_secret : 'client_secret',
102
104
  redirect_uris : [ callbackURI ],
103
- /* eslint-enable camelcase */
104
105
  },
105
106
  };
106
107
 
107
108
  const credentialsJSON: GoogleApis.Auth.Credentials = {
108
- // eslint-disable-next-line camelcase
109
109
  access_token : 'access_token222',
110
110
  };
111
111
 
112
- let json: any;
112
+ let json: unknown;
113
113
 
114
114
  const code = 'code';
115
115
  const authUrl = 'https://authUrl';
@@ -120,26 +120,14 @@ const auth = {
120
120
 
121
121
  let exists: boolean;
122
122
 
123
- let getJSONSpy: jest.SpyInstance;
124
- let getJSONAsyncSpy: jest.SpyInstance;
125
- let readJSONSpy: jest.SpyInstance;
126
-
127
- beforeAll(() => {
128
- getJSONSpy = jest.spyOn(fs, 'getJSON');
129
- getJSONAsyncSpy = jest.spyOn(fs, 'getJSONAsync');
130
- readJSONSpy = jest.spyOn(fs, 'readJSON');
131
- });
123
+ const getJSONSpy = jest.spyOn(fs, 'getJSON').mockReturnValue(json);
124
+ const getJSONAsyncSpy = jest.spyOn(fs, 'getJSONAsync').mockResolvedValue(json);
125
+ const readJSONSpy = jest.spyOn(fs, 'readJSON').mockReturnValue(json);
132
126
 
133
127
  beforeEach(() => {
134
- getJSONSpy.mockImplementation(() => json);
135
- getJSONAsyncSpy.mockImplementation(() => json);
136
- readJSONSpy.mockImplementation(() => json);
137
- });
138
-
139
- afterAll(() => {
140
- getJSONSpy.mockRestore();
141
- getJSONAsyncSpy.mockRestore();
142
- readJSONSpy.mockRestore();
128
+ getJSONSpy.mockReturnValue(json);
129
+ getJSONAsyncSpy.mockResolvedValue(json);
130
+ readJSONSpy.mockReturnValue(json);
143
131
  });
144
132
 
145
133
  describe('src/lib/secrets', () => {
@@ -152,13 +140,13 @@ describe('src/lib/secrets', () => {
152
140
  original.getScopes();
153
141
 
154
142
  expect(getJSONSpy).toHaveBeenCalled();
155
- expect(getJSONSpy.mock.calls[0][0]).toEqual(scopesFile);
143
+ expect(getJSONSpy.mock.calls[0]?.[0]).toEqual(scopesFile);
156
144
  });
157
145
 
158
146
  it('should fallback to error', () => {
159
147
  original.getScopes();
160
148
 
161
- expect(getJSONSpy.mock.calls[0][1]).toThrow(scopesError);
149
+ expect(getJSONSpy.mock.calls[0]?.[1]).toThrow(scopesError);
162
150
  });
163
151
 
164
152
  it('should return scopes', () => {
@@ -177,13 +165,13 @@ describe('src/lib/secrets', () => {
177
165
  original.getSecrets(profile);
178
166
 
179
167
  expect(getJSONSpy).toHaveBeenCalled();
180
- expect(getJSONSpy.mock.calls[0][0]).toEqual(secretsFile);
168
+ expect(getJSONSpy.mock.calls[0]?.[0]).toEqual(secretsFile);
181
169
  });
182
170
 
183
171
  it('should fallback to error', () => {
184
172
  original.getSecrets(profile);
185
173
 
186
- expect(getJSONSpy.mock.calls[0][1]).toThrow(secretsError);
174
+ expect(getJSONSpy.mock.calls[0]?.[1]).toThrow(secretsError);
187
175
  });
188
176
 
189
177
  it('should check secrets', () => {
@@ -209,14 +197,14 @@ describe('src/lib/secrets', () => {
209
197
  await original.getCredentials(profile, auth);
210
198
 
211
199
  expect(getJSONAsyncSpy).toHaveBeenCalled();
212
- expect(getJSONAsyncSpy.mock.calls[0][0]).toEqual(credentialsFile);
200
+ expect(getJSONAsyncSpy.mock.calls[0]?.[0]).toEqual(credentialsFile);
213
201
  });
214
202
 
215
203
  it('should get json from credentials file if temporariness not set', async () => {
216
204
  await original.getCredentials(profile, auth, { temporary : false });
217
205
 
218
206
  expect(getJSONAsyncSpy).toHaveBeenCalled();
219
- expect(getJSONAsyncSpy.mock.calls[0][0]).toEqual(credentialsFile);
207
+ expect(getJSONAsyncSpy.mock.calls[0]?.[0]).toEqual(credentialsFile);
220
208
  });
221
209
 
222
210
  it('should not get json from credentials file if temporariness set', async () => {
@@ -232,8 +220,8 @@ describe('src/lib/secrets', () => {
232
220
 
233
221
  expect(secrets.createCredentials).not.toHaveBeenCalled();
234
222
 
235
- const fallback = getJSONAsyncSpy.mock.calls[0][1];
236
- const result = await fallback();
223
+ const fallback = getJSONAsyncSpy.mock.calls[0]?.[1];
224
+ const result = await fallback?.();
237
225
 
238
226
  expect(readJSONSpy).not.toHaveBeenCalled();
239
227
  expect(secrets.createCredentials).toHaveBeenCalledWith(profile, auth, undefined, 'consent');
@@ -247,8 +235,8 @@ describe('src/lib/secrets', () => {
247
235
 
248
236
  expect(secrets.createCredentials).not.toHaveBeenCalled();
249
237
 
250
- const fallback = getJSONAsyncSpy.mock.calls[0][1];
251
- const result = await fallback();
238
+ const fallback = getJSONAsyncSpy.mock.calls[0]?.[1];
239
+ const result = await fallback?.();
252
240
 
253
241
  expect(readJSONSpy).not.toHaveBeenCalled();
254
242
  expect(secrets.createCredentials).toHaveBeenCalledWith(profile, auth, { temporary : false }, 'consent');
@@ -262,8 +250,8 @@ describe('src/lib/secrets', () => {
262
250
 
263
251
  expect(secrets.createCredentials).not.toHaveBeenCalled();
264
252
 
265
- const fallback = getJSONAsyncSpy.mock.calls[0][1];
266
- const result = await fallback();
253
+ const fallback = getJSONAsyncSpy.mock.calls[0]?.[1];
254
+ const result = await fallback?.();
267
255
 
268
256
  expect(readJSONSpy).toHaveBeenCalledWith(credentialsFile);
269
257
  expect(secrets.createCredentials).toHaveBeenCalledWith(profile, auth, undefined, 'consent');
@@ -272,37 +260,31 @@ describe('src/lib/secrets', () => {
272
260
 
273
261
  it('should call createCredentials without consent in fallback and replace refresh_token if existing credentials have refresh token', async () => {
274
262
  exists = true;
275
- // eslint-disable-next-line camelcase
276
263
  readJSONSpy.mockReturnValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token' });
277
264
 
278
265
  await original.getCredentials(profile, auth);
279
266
 
280
267
  expect(secrets.createCredentials).not.toHaveBeenCalled();
281
268
 
282
- const fallback = getJSONAsyncSpy.mock.calls[0][1];
283
- const result = await fallback();
269
+ const fallback = getJSONAsyncSpy.mock.calls[0]?.[1];
270
+ const result = await fallback?.();
284
271
 
285
272
  expect(readJSONSpy).toHaveBeenCalledWith(credentialsFile);
286
- expect(secrets.createCredentials).toHaveBeenCalledWith(profile, auth, undefined, undefined);
287
- // eslint-disable-next-line camelcase
288
273
  expect(result).toEqual({ ... credentialsJSON, refresh_token : 'refresh_token' });
289
274
  });
290
275
 
291
276
  it('should call createCredentials without consent in fallback and leave refresh token if existing credentials have refresh token', async () => {
292
277
  exists = true;
293
- // eslint-disable-next-line camelcase
294
278
  readJSONSpy.mockReturnValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token' });
295
- // eslint-disable-next-line camelcase
296
279
  jest.spyOn(secrets, 'createCredentials').mockResolvedValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token_exists' });
297
280
 
298
281
  await original.getCredentials(profile, auth);
299
282
 
300
- const fallback = getJSONAsyncSpy.mock.calls[0][1];
301
- const result = await fallback();
283
+ const fallback = getJSONAsyncSpy.mock.calls[0]?.[1];
284
+ const result = await fallback?.();
302
285
 
303
286
  expect(readJSONSpy).toHaveBeenCalledWith(credentialsFile);
304
287
  expect(secrets.createCredentials).toHaveBeenCalledWith(profile, auth, undefined, undefined);
305
- // eslint-disable-next-line camelcase
306
288
  expect(result).toEqual({ ...credentialsJSON, refresh_token : 'refresh_token_exists' });
307
289
  });
308
290
  });
@@ -313,26 +295,22 @@ describe('src/lib/secrets', () => {
313
295
  });
314
296
 
315
297
  it('should return false if no refresh token', async () => {
316
- // eslint-disable-next-line camelcase
317
298
  expect(await original.validateCredentials({ access_token : 'token' })).toEqual({ isValid : false, validationError : 'Credentials does not have refresh_token' });
318
299
  });
319
300
 
320
301
  it('should return false if no expiration date', async () => {
321
- // eslint-disable-next-line camelcase
322
302
  expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token' })).toEqual({ isValid : false, validationError : 'Credentials does not have expiry_date' });
323
303
  });
324
304
 
325
305
  it('should return true if credentials are not more than 1 week ago', async () => {
326
306
  const expiryDate = new Date();
327
307
  expiryDate.setDate(expiryDate.getDate() - 6);
328
- // eslint-disable-next-line camelcase
329
308
  expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token', expiry_date : expiryDate.getTime() })).toEqual({ isValid : true });
330
309
  });
331
310
 
332
311
  it('should return false if credentials are more than 1 week ago', async () => {
333
312
  const expiryDate = new Date();
334
313
  expiryDate.setDate(expiryDate.getDate() - 8);
335
- // eslint-disable-next-line camelcase
336
314
  expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token', expiry_date : expiryDate.getTime() })).toEqual({ isValid : false, validationError : 'Credentials expired' });
337
315
  });
338
316
  });
@@ -370,11 +348,10 @@ describe('src/lib/secrets', () => {
370
348
  });
371
349
 
372
350
  it('should generate authUrl', async () => {
373
- original.createCredentials(profile, auth);
351
+ void original.createCredentials(profile, auth);
374
352
  await Promise.resolve();
375
353
 
376
354
  expect(auth.generateAuthUrl).toHaveBeenCalledWith({
377
- // eslint-disable-next-line camelcase
378
355
  access_type : 'offline',
379
356
  prompt : undefined,
380
357
  scope : [
@@ -385,11 +362,10 @@ describe('src/lib/secrets', () => {
385
362
  });
386
363
 
387
364
  it('should generate authUrl and require consent if explicitly asked', async () => {
388
- original.createCredentials(profile, auth, { temporary : true }, 'consent');
365
+ void original.createCredentials(profile, auth, { temporary : true }, 'consent');
389
366
  await Promise.resolve();
390
367
 
391
368
  expect(auth.generateAuthUrl).toHaveBeenCalledWith({
392
- // eslint-disable-next-line camelcase
393
369
  access_type : 'offline',
394
370
  prompt : 'consent',
395
371
  scope : [
@@ -400,11 +376,10 @@ describe('src/lib/secrets', () => {
400
376
  });
401
377
 
402
378
  it('should generate authUrl with custom scopes', async () => {
403
- original.createCredentials(profile, auth, { scopes : [ 'scope1', 'scope2' ] });
379
+ void original.createCredentials(profile, auth, { scopes : [ 'scope1', 'scope2' ] });
404
380
  await Promise.resolve();
405
381
 
406
382
  expect(auth.generateAuthUrl).toHaveBeenCalledWith({
407
- // eslint-disable-next-line camelcase
408
383
  access_type : 'offline',
409
384
  prompt : undefined,
410
385
  scope : [ 'scope1', 'scope2' ],
@@ -412,7 +387,7 @@ describe('src/lib/secrets', () => {
412
387
  });
413
388
 
414
389
  it('should create server on 6006 port', async () => {
415
- original.createCredentials(profile, auth);
390
+ void original.createCredentials(profile, auth);
416
391
  await Promise.resolve();
417
392
 
418
393
  expect(http.createServer).toHaveBeenCalled();
@@ -420,7 +395,7 @@ describe('src/lib/secrets', () => {
420
395
  });
421
396
 
422
397
  it('should open browser page and warn about it once listening', async () => {
423
- original.createCredentials(profile, auth);
398
+ void original.createCredentials(profile, auth);
424
399
  await Promise.resolve();
425
400
 
426
401
  server.emit('listening');
@@ -430,7 +405,7 @@ describe('src/lib/secrets', () => {
430
405
  });
431
406
 
432
407
  it('should not open browser page and warn about it until listening', async () => {
433
- original.createCredentials(profile, auth);
408
+ void original.createCredentials(profile, auth);
434
409
  await Promise.resolve();
435
410
 
436
411
  expect(open).not.toHaveBeenCalled();
@@ -438,7 +413,7 @@ describe('src/lib/secrets', () => {
438
413
  });
439
414
 
440
415
  it('should show nothing on the browser page if request.url is empty', async () => {
441
- original.createCredentials(profile, auth);
416
+ void original.createCredentials(profile, auth);
442
417
  makeRequest('');
443
418
  await Promise.resolve();
444
419
 
@@ -446,7 +421,7 @@ describe('src/lib/secrets', () => {
446
421
  });
447
422
 
448
423
  it('should show opening instructions if opened the home page', async () => {
449
- original.createCredentials(profile, auth);
424
+ void original.createCredentials(profile, auth);
450
425
  makeRequest('/');
451
426
  await Promise.resolve();
452
427
 
@@ -454,7 +429,7 @@ describe('src/lib/secrets', () => {
454
429
  });
455
430
 
456
431
  it('should ask to close webpage', async () => {
457
- original.createCredentials(profile, auth);
432
+ void original.createCredentials(profile, auth);
458
433
  makeRequest(tokenUrl);
459
434
  await Promise.resolve();
460
435
 
@@ -462,13 +437,15 @@ describe('src/lib/secrets', () => {
462
437
  });
463
438
 
464
439
  it('should close server and destroy all connections if request.url is truthy', async () => {
465
- original.createCredentials(profile, auth);
440
+ void original.createCredentials(profile, auth);
466
441
  makeRequest(tokenUrl);
467
442
  await Promise.resolve();
468
443
 
469
444
  expect(server.close).toHaveBeenCalled();
470
445
 
471
- connections.forEach((connection) => expect(connection.destroy).toHaveBeenCalled());
446
+ connections.forEach((connection) => {
447
+ expect(connection.destroy).toHaveBeenCalled();
448
+ });
472
449
  });
473
450
 
474
451
  it('should close server and resolve if request.url is truthy', async () => {
@@ -480,7 +457,7 @@ describe('src/lib/secrets', () => {
480
457
  });
481
458
 
482
459
  it('should not close server if request.url is falsy', async () => {
483
- original.createCredentials(profile, auth);
460
+ void original.createCredentials(profile, auth);
484
461
  makeRequest(undefined);
485
462
  await Promise.resolve();
486
463
 
@@ -490,14 +467,14 @@ describe('src/lib/secrets', () => {
490
467
  it('should re-throw a server error if error is not EADDRINUSE', () => {
491
468
  const error = { code : 'RANDOM', message : 'random error' } as NodeJS.ErrnoException;
492
469
 
493
- original.createCredentials(profile, auth);
470
+ void original.createCredentials(profile, auth);
494
471
  expect(() => server.emit('error', error)).toThrow(error.message);
495
472
  });
496
473
 
497
474
  it('should not re-throw a server error and try to listen again in 1000 seconds if error is EADDRINUSE', () => {
498
475
  const error = { code : 'EADDRINUSE' } as NodeJS.ErrnoException;
499
476
 
500
- original.createCredentials(profile, auth);
477
+ void original.createCredentials(profile, auth);
501
478
  expect(server.listen).toHaveBeenCalledTimes(1);
502
479
  expect(() => server.emit('error', error)).not.toThrow();
503
480
  expect(server.listen).toHaveBeenCalledTimes(1);
@@ -538,7 +515,7 @@ describe('src/lib/secrets', () => {
538
515
  it('should output error if redirect_uri is incorrect', () => {
539
516
  const wrongSecretsJSON = { ...secretsJSON };
540
517
  wrongSecretsJSON.web.redirect_uris[0] = wrongRedirectURI;
541
- const func = () => original.checkSecrets(profile, wrongSecretsJSON, secretsFile);
518
+ const func = (): true => original.checkSecrets(profile, wrongSecretsJSON, secretsFile);
542
519
 
543
520
  expect(func).toThrow('Error in credentials file: redirect URI should be http://localhost:6006/oauthcallback.\nsecretsError');
544
521
  });
package/src/lib/api.ts CHANGED
@@ -1,43 +1,48 @@
1
1
  import type GoogleApis from 'googleapis';
2
2
  import { log, warn } from '@anmiles/logger';
3
3
  import sleep from '@anmiles/sleep';
4
- import type { AuthOptions, CommonOptions } from '../types';
4
+ import type { AuthOptions, CommonOptions } from '../types/options';
5
5
  import { getAuth } from './auth';
6
6
  import { deleteCredentials } from './secrets';
7
+ import '@anmiles/prototypes';
7
8
 
8
9
  const requestInterval = 300;
9
10
 
10
- type CommonAPI<TItem> = {
11
- list: {
12
- (params?: { pageToken: string | undefined }, options?: GoogleApis.Common.MethodOptions): Promise<GoogleApis.Common.GaxiosResponse<CommonResponse<TItem>>>;
13
- (callback: (err: Error | null, res?: GoogleApis.Common.GaxiosResponse<CommonResponse<TItem>> | null) => void): void;
14
- }
11
+ type ListParams = Record<string, unknown> & {
12
+ pageToken : string | undefined;
15
13
  };
16
14
 
17
- type CommonResponse<TItem> = {
18
- items?: TItem[],
15
+ interface CommonAPI<TItem> {
16
+ list : (
17
+ params?: ListParams,
18
+ options?: GoogleApis.Common.MethodOptions
19
+ ) => Promise<GoogleApis.Common.GaxiosResponse<CommonResponse<TItem>>>;
20
+ }
21
+
22
+ interface CommonResponse<TItem> {
23
+ items? : TItem[];
19
24
  pageInfo?: {
20
- totalResults?: number | null | undefined
21
- },
22
- nextPageToken?: string | null | undefined
23
- };
25
+ totalResults? : number | null | undefined;
26
+ };
27
+ nextPageToken? : string | null | undefined;
28
+ }
24
29
 
25
30
  class API<TGoogleAPI> {
26
- private auth: GoogleApis.Common.OAuth2Client | undefined;
27
- api: TGoogleAPI | undefined;
31
+ api : TGoogleAPI | undefined;
32
+ private auth : GoogleApis.Common.OAuth2Client | undefined;
28
33
 
29
34
  constructor(
30
- private getter: (auth: GoogleApis.Common.OAuth2Client) => TGoogleAPI,
31
- private profile: string,
32
- private authOptions?: AuthOptions,
35
+ private readonly getter: (auth: GoogleApis.Common.OAuth2Client) => TGoogleAPI,
36
+ private readonly profile: string,
37
+ private readonly authOptions?: AuthOptions,
33
38
  ) { }
34
39
 
35
- async init() {
40
+ async init(): Promise<void> {
36
41
  this.auth = await getAuth(this.profile, this.authOptions);
37
42
  this.api = this.getter(this.auth);
38
43
  }
39
44
 
40
- async getItems<TItem>(selectAPI: (api: TGoogleAPI) => CommonAPI<TItem>, params: any, options?: CommonOptions): Promise<TItem[]> {
45
+ async getItems<TItem>(selectAPI: (api: TGoogleAPI) => CommonAPI<TItem>, params: object, options?: CommonOptions): Promise<TItem[]> {
41
46
  const items: TItem[] = [];
42
47
 
43
48
  let pageToken: string | null | undefined = undefined;
@@ -47,14 +52,14 @@ class API<TGoogleAPI> {
47
52
 
48
53
  try {
49
54
  if (!this.api) {
50
- throw 'API is not initialized. Call `init` before getting items.';
55
+ throw new Error('API is not initialized. Call `init` before getting items.');
51
56
  }
52
57
 
53
58
  const selectedAPI = selectAPI(this.api);
54
59
 
55
60
  response = await selectedAPI.list({ ...params, pageToken });
56
61
  } catch (ex) {
57
- const message = ex instanceof Error ? ex.message : ex as string;
62
+ const { message } = Error.parse(ex);
58
63
 
59
64
  if ((message === 'invalid_grant' || message === 'Invalid credentials') && !this.authOptions?.temporary) {
60
65
  warn('Access token stored is invalid, re-creating...');
@@ -69,7 +74,7 @@ class API<TGoogleAPI> {
69
74
  response.data.items?.forEach((item) => items.push(item));
70
75
 
71
76
  if (!options?.hideProgress) {
72
- log(`Getting items (${items.length} of ${response.data.pageInfo?.totalResults || 'many'})...`);
77
+ log(`Getting items (${items.length} of ${response.data.pageInfo?.totalResults ?? 'many'})...`);
73
78
  }
74
79
 
75
80
  await sleep(requestInterval);
@@ -80,12 +85,16 @@ class API<TGoogleAPI> {
80
85
  }
81
86
  }
82
87
 
83
- async function getAPI<TGoogleAPI>(getter: (auth: GoogleApis.Common.OAuth2Client) => TGoogleAPI, profile: string, authOptions?: AuthOptions): Promise<API<TGoogleAPI>> {
88
+ async function getAPI<TGoogleAPI>(
89
+ getter: (auth: GoogleApis.Common.OAuth2Client) => TGoogleAPI,
90
+ profile: string,
91
+ authOptions?: AuthOptions,
92
+ ): Promise<API<TGoogleAPI>> {
84
93
  if (!authOptions?.temporary) {
85
- const writableScopes = authOptions?.scopes?.filter((scope) => !scope.endsWith('.readonly')) || [];
94
+ const writableScopes = authOptions?.scopes?.filter((scope) => !scope.endsWith('.readonly')) ?? [];
86
95
 
87
96
  if (writableScopes.length > 0) {
88
- warn(`WARNING: trying to create permanent credentials using non-readonly scopes (${writableScopes}). Permanent credentials will be stored in the file and potentially might be re-used to modify your data`);
97
+ warn(`WARNING: trying to create permanent credentials using non-readonly scopes (${writableScopes.join(', ')}). Permanent credentials will be stored in the file and potentially might be re-used to modify your data`);
89
98
  }
90
99
  }
91
100
 
@@ -94,5 +103,6 @@ async function getAPI<TGoogleAPI>(getter: (auth: GoogleApis.Common.OAuth2Client)
94
103
  return instance;
95
104
  }
96
105
 
97
- export { getAPI };
106
+ export { getAPI, API };
107
+ export type { CommonResponse };
98
108
  export default { getAPI, API };
package/src/lib/auth.ts CHANGED
@@ -1,16 +1,13 @@
1
1
  import { google } from 'googleapis';
2
2
  import type GoogleApis from 'googleapis';
3
3
  import { info, warn } from '@anmiles/logger';
4
- import type { CommonOptions, AuthOptions } from '../types';
4
+ import type { CommonOptions, AuthOptions } from '../types/options';
5
5
  import { getProfiles } from './profiles';
6
6
  import { getCredentials, getSecrets } from './secrets';
7
7
 
8
8
  import auth from './auth';
9
9
 
10
- export { login, getAuth };
11
- export default { login, getAuth };
12
-
13
- async function login(profile?: string, options?: CommonOptions & AuthOptions): Promise<void> {
10
+ async function login(profile?: string, options?: AuthOptions & CommonOptions): Promise<void> {
14
11
  const profiles = getProfiles().filter((p) => !profile || p === profile);
15
12
 
16
13
  for (const profile of profiles) {
@@ -40,3 +37,6 @@ async function getAuth(profile: string, options?: AuthOptions): Promise<GoogleAp
40
37
  google.options({ auth : googleAuth });
41
38
  return googleAuth;
42
39
  }
40
+
41
+ export { login, getAuth };
42
+ export default { login, getAuth };
package/src/lib/paths.ts CHANGED
@@ -1,9 +1,6 @@
1
1
  import path from 'path';
2
2
  import type { templates } from './renderer';
3
3
 
4
- export { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile, getTemplateFile };
5
- export default { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile, getTemplateFile };
6
-
7
4
  const dirPaths = {
8
5
  input : 'input',
9
6
  secrets : 'secrets',
@@ -11,22 +8,25 @@ const dirPaths = {
11
8
  templates : 'node_modules/@anmiles/google-api-wrapper/dist/templates',
12
9
  };
13
10
 
14
- function getProfilesFile() {
11
+ function getProfilesFile(): string {
15
12
  return path.join(dirPaths.input, 'profiles.json');
16
13
  }
17
14
 
18
- function getScopesFile() {
15
+ function getScopesFile(): string {
19
16
  return 'scopes.json';
20
17
  }
21
18
 
22
- function getSecretsFile(profile: string) {
19
+ function getSecretsFile(profile: string): string {
23
20
  return path.join(dirPaths.secrets, `${profile}.json`);
24
21
  }
25
22
 
26
- function getCredentialsFile(profile: string) {
23
+ function getCredentialsFile(profile: string): string {
27
24
  return path.join(dirPaths.secrets, `${profile}.credentials.json`);
28
25
  }
29
26
 
30
- function getTemplateFile(templateName: keyof typeof templates) {
27
+ function getTemplateFile(templateName: keyof typeof templates): string {
31
28
  return path.join(dirPaths.templates, `${templateName}.html`);
32
29
  }
30
+
31
+ export { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile, getTemplateFile };
32
+ export default { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile, getTemplateFile };
@@ -4,9 +4,6 @@ import { getProfilesFile } from './paths';
4
4
 
5
5
  import profiles from './profiles';
6
6
 
7
- export { getProfiles, setProfiles, createProfile, filterProfiles };
8
- export default { getProfiles, setProfiles, createProfile, filterProfiles };
9
-
10
7
  function getProfiles(): string[] {
11
8
  const profilesFile = getProfilesFile();
12
9
  return fs.getJSON(profilesFile, () => []);
@@ -19,7 +16,7 @@ function setProfiles(profiles: string[]): void {
19
16
 
20
17
  function createProfile(profile?: string): void {
21
18
  if (!profile) {
22
- throw 'Usage: `npm run create <profile>` where `profile` - is any profile name you want';
19
+ throw new Error('Usage: `npm run create <profile>` where `profile` - is any profile name you want');
23
20
  }
24
21
 
25
22
  const existingProfiles = profiles.getProfiles();
@@ -36,7 +33,7 @@ function filterProfiles(profile?: string): string[] {
36
33
  const existingProfiles = profiles.getProfiles();
37
34
 
38
35
  if (existingProfiles.length === 0) {
39
- throw 'Please `npm run create` at least one profile';
36
+ throw new Error('Please `npm run create` at least one profile');
40
37
  }
41
38
 
42
39
  if (!profile) {
@@ -47,5 +44,8 @@ function filterProfiles(profile?: string): string[] {
47
44
  return [ profile ];
48
45
  }
49
46
 
50
- throw `Profile '${profile}' does not exist`;
47
+ throw new Error(`Profile '${profile}' does not exist`);
51
48
  }
49
+
50
+ export { getProfiles, setProfiles, createProfile, filterProfiles };
51
+ export default { getProfiles, setProfiles, createProfile, filterProfiles };