@anmiles/google-api-wrapper 2.1.3 → 3.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/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ 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
+ ## [3.0.1](../../tags/v3.0.1) - 2023-03-12
9
+ ### Changed
10
+ - Fixed path to scopes file
11
+
12
+ ## [3.0.0](../../tags/v3.0.0) - 2023-03-13
13
+ ### Changed
14
+ - Revised auth instructions
15
+ - Scopes can be set per end-project
16
+
8
17
  ## [2.1.3](../../tags/v2.1.3) - 2023-03-13
9
18
  ### Changed
10
19
  - Fixed exported types
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export * as calendar from './lib/api/calendar';
2
2
  export * as youtube from './lib/api/youtube';
3
3
  export { getItems } from './lib/api/shared';
4
4
  export { createProfile, getProfiles } from './lib/profiles';
5
- export { login } from './lib/auth';
5
+ export { login, getAuth } from './lib/auth';
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.login = exports.getProfiles = exports.createProfile = exports.getItems = exports.youtube = exports.calendar = void 0;
26
+ exports.getAuth = exports.login = exports.getProfiles = exports.createProfile = exports.getItems = exports.youtube = exports.calendar = void 0;
27
27
  exports.calendar = __importStar(require("./lib/api/calendar"));
28
28
  exports.youtube = __importStar(require("./lib/api/youtube"));
29
29
  var shared_1 = require("./lib/api/shared");
@@ -33,4 +33,5 @@ Object.defineProperty(exports, "createProfile", { enumerable: true, get: functio
33
33
  Object.defineProperty(exports, "getProfiles", { enumerable: true, get: function () { return profiles_1.getProfiles; } });
34
34
  var auth_1 = require("./lib/auth");
35
35
  Object.defineProperty(exports, "login", { enumerable: true, get: function () { return auth_1.login; } });
36
+ Object.defineProperty(exports, "getAuth", { enumerable: true, get: function () { return auth_1.getAuth; } });
36
37
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+DAA+C;AAC/C,6DAA6C;AAC7C,2CAA4C;AAAnC,kGAAA,QAAQ,OAAA;AACjB,2CAA4D;AAAnD,yGAAA,aAAa,OAAA;AAAE,uGAAA,WAAW,OAAA;AACnC,mCAAmC;AAA1B,6FAAA,KAAK,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+DAA+C;AAC/C,6DAA6C;AAC7C,2CAA4C;AAAnC,kGAAA,QAAQ,OAAA;AACjB,2CAA4D;AAAnD,yGAAA,aAAa,OAAA;AAAE,uGAAA,WAAW,OAAA;AACnC,mCAA4C;AAAnC,6FAAA,KAAK,OAAA;AAAE,+FAAA,OAAO,OAAA"}
package/dist/lib/auth.js CHANGED
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getAuth = exports.login = void 0;
7
7
  const googleapis_1 = require("googleapis");
8
+ const logger_1 = require("./logger");
8
9
  const profiles_1 = require("./profiles");
9
10
  const secrets_1 = require("./secrets");
10
11
  const auth_1 = __importDefault(require("./auth"));
@@ -12,12 +13,14 @@ exports.default = { login, getAuth };
12
13
  async function login(profile) {
13
14
  const profiles = (0, profiles_1.getProfiles)().filter((p) => !profile || p === profile);
14
15
  for (const profile of profiles) {
16
+ (0, logger_1.warn)(`${profile} - logging in...`);
15
17
  await auth_1.default.getAuth(profile);
18
+ (0, logger_1.info)(`${profile} - logged in successfully`);
16
19
  }
17
20
  }
18
21
  exports.login = login;
19
22
  async function getAuth(profile) {
20
- const secrets = await (0, secrets_1.getSecrets)(profile);
23
+ const secrets = (0, secrets_1.getSecrets)(profile);
21
24
  const googleAuth = new googleapis_1.google.auth.OAuth2(secrets.web.client_id, secrets.web.client_secret, secrets.web.redirect_uris[0]);
22
25
  const tokens = await (0, secrets_1.getCredentials)(profile, googleAuth);
23
26
  googleAuth.setCredentials(tokens);
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":";;;;;;AAAA,2CAAoC;AAEpC,yCAAyC;AACzC,uCAAuD;AAEvD,kDAA0B;AAG1B,kBAAe,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAElC,KAAK,UAAU,KAAK,CAAC,OAAgB;IACpC,MAAM,QAAQ,GAAG,IAAA,sBAAW,GAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC;IAExE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;QAC/B,MAAM,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;KAC5B;AACF,CAAC;AATQ,sBAAK;AAWd,KAAK,UAAU,OAAO,CAAC,OAAe;IACrC,MAAM,OAAO,GAAG,MAAM,IAAA,oBAAU,EAAC,OAAO,CAAC,CAAC;IAE1C,MAAM,UAAU,GAAG,IAAI,mBAAM,CAAC,IAAI,CAAC,MAAM,CACxC,OAAO,CAAC,GAAG,CAAC,SAAS,EACrB,OAAO,CAAC,GAAG,CAAC,aAAa,EACzB,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAC5B,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,IAAA,wBAAc,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACzD,UAAU,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,UAAU,CAAC;AACnB,CAAC;AAvBe,0BAAO"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":";;;;;;AAAA,2CAAoC;AAEpC,qCAAsC;AACtC,yCAAyC;AACzC,uCAAuD;AAEvD,kDAA0B;AAG1B,kBAAe,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAElC,KAAK,UAAU,KAAK,CAAC,OAAgB;IACpC,MAAM,QAAQ,GAAG,IAAA,sBAAW,GAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC;IAExE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;QAC/B,IAAA,aAAI,EAAC,GAAG,OAAO,kBAAkB,CAAC,CAAC;QACnC,MAAM,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAA,aAAI,EAAC,GAAG,OAAO,2BAA2B,CAAC,CAAC;KAC5C;AACF,CAAC;AAXQ,sBAAK;AAad,KAAK,UAAU,OAAO,CAAC,OAAe;IACrC,MAAM,OAAO,GAAG,IAAA,oBAAU,EAAC,OAAO,CAAC,CAAC;IAEpC,MAAM,UAAU,GAAG,IAAI,mBAAM,CAAC,IAAI,CAAC,MAAM,CACxC,OAAO,CAAC,GAAG,CAAC,SAAS,EACrB,OAAO,CAAC,GAAG,CAAC,aAAa,EACzB,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAC5B,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,IAAA,wBAAc,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACzD,UAAU,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,UAAU,CAAC;AACnB,CAAC;AAzBe,0BAAO"}
@@ -1,8 +1,9 @@
1
- export { ensureDir, ensureFile, getProfilesFile, getSecretsFile, getCredentialsFile };
1
+ export { ensureDir, ensureFile, getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile };
2
2
  declare const _default: {
3
3
  ensureDir: typeof ensureDir;
4
4
  ensureFile: typeof ensureFile;
5
5
  getProfilesFile: typeof getProfilesFile;
6
+ getScopesFile: typeof getScopesFile;
6
7
  getSecretsFile: typeof getSecretsFile;
7
8
  getCredentialsFile: typeof getCredentialsFile;
8
9
  };
@@ -10,5 +11,6 @@ export default _default;
10
11
  declare function ensureDir(dirPath: string): string;
11
12
  declare function ensureFile(filePath: string): string;
12
13
  declare function getProfilesFile(): string;
14
+ declare function getScopesFile(): string;
13
15
  declare function getSecretsFile(profile: string): string;
14
16
  declare function getCredentialsFile(profile: string): string;
package/dist/lib/paths.js CHANGED
@@ -3,11 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getCredentialsFile = exports.getSecretsFile = exports.getProfilesFile = exports.ensureFile = exports.ensureDir = void 0;
6
+ exports.getCredentialsFile = exports.getSecretsFile = exports.getScopesFile = exports.getProfilesFile = exports.ensureFile = exports.ensureDir = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const paths_1 = __importDefault(require("./paths"));
10
- exports.default = { ensureDir, ensureFile, getProfilesFile, getSecretsFile, getCredentialsFile };
10
+ exports.default = { ensureDir, ensureFile, getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile };
11
11
  const dirPaths = {
12
12
  input: 'input',
13
13
  secrets: 'secrets',
@@ -31,6 +31,10 @@ function getProfilesFile() {
31
31
  return path_1.default.join(dirPaths.input, 'profiles.json');
32
32
  }
33
33
  exports.getProfilesFile = getProfilesFile;
34
+ function getScopesFile() {
35
+ return path_1.default.join(dirPaths.input, 'scopes.json');
36
+ }
37
+ exports.getScopesFile = getScopesFile;
34
38
  function getSecretsFile(profile) {
35
39
  return path_1.default.join(dirPaths.secrets, `${profile}.json`);
36
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,oDAA4B;AAG5B,kBAAe,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;AAE9F,MAAM,QAAQ,GAAG;IAChB,KAAK,EAAK,OAAO;IACjB,OAAO,EAAG,SAAS;CACnB,CAAC;AAEF,SAAS,SAAS,CAAC,OAAe;IACjC,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QAC5B,YAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAG,IAAI,EAAE,CAAC,CAAC;KAC5C;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAbQ,8BAAS;AAelB,SAAS,UAAU,CAAC,QAAgB;IACnC,eAAK,CAAC,SAAS,CAAC,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAExC,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC7B,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;KAC/B;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAtBmB,gCAAU;AAwB9B,SAAS,eAAe;IACvB,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;AACnD,CAAC;AA1B+B,0CAAe;AA4B/C,SAAS,cAAc,CAAC,OAAe;IACtC,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;AACvD,CAAC;AA9BgD,wCAAc;AAgC/D,SAAS,kBAAkB,CAAC,OAAe;IAC1C,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,mBAAmB,CAAC,CAAC;AACnE,CAAC;AAlCgE,gDAAkB"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,oDAA4B;AAG5B,kBAAe,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;AAE7G,MAAM,QAAQ,GAAG;IAChB,KAAK,EAAK,OAAO;IACjB,OAAO,EAAG,SAAS;CACnB,CAAC;AAEF,SAAS,SAAS,CAAC,OAAe;IACjC,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QAC5B,YAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAG,IAAI,EAAE,CAAC,CAAC;KAC5C;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAbQ,8BAAS;AAelB,SAAS,UAAU,CAAC,QAAgB;IACnC,eAAK,CAAC,SAAS,CAAC,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAExC,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC7B,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;KAC/B;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAtBmB,gCAAU;AAwB9B,SAAS,eAAe;IACvB,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;AACnD,CAAC;AA1B+B,0CAAe;AA4B/C,SAAS,aAAa;IACrB,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;AACjD,CAAC;AA9BgD,sCAAa;AAgC9D,SAAS,cAAc,CAAC,OAAe;IACtC,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;AACvD,CAAC;AAlC+D,wCAAc;AAoC9E,SAAS,kBAAkB,CAAC,OAAe;IAC1C,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,mBAAmB,CAAC,CAAC;AACnE,CAAC;AAtC+E,gDAAkB"}
@@ -2,15 +2,19 @@ import type GoogleApis from 'googleapis';
2
2
  import type { Secrets } from '../types';
3
3
  export { getSecrets, getCredentials };
4
4
  declare const _default: {
5
+ getScopes: typeof getScopes;
5
6
  getSecrets: typeof getSecrets;
6
7
  getCredentials: typeof getCredentials;
7
8
  createCredentials: typeof createCredentials;
8
9
  checkSecrets: typeof checkSecrets;
9
10
  getSecretsError: typeof getSecretsError;
11
+ getScopesError: typeof getScopesError;
10
12
  };
11
13
  export default _default;
12
- declare function getSecrets(profile: string): Promise<Secrets>;
14
+ declare function getScopes(): string[];
15
+ declare function getSecrets(profile: string): Secrets;
13
16
  declare function getCredentials(profile: string, auth: GoogleApis.Common.OAuth2Client): Promise<GoogleApis.Auth.Credentials>;
14
17
  declare function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client): Promise<GoogleApis.Auth.Credentials>;
15
18
  declare function checkSecrets(profile: string, secretsObject: Secrets, secretsFile: string): true | void;
19
+ declare function getScopesError(scopesFile: string): string;
16
20
  declare function getSecretsError(profile: string, secretsFile: string): string;
@@ -33,11 +33,15 @@ const jsonLib_1 = require("./jsonLib");
33
33
  const logger_1 = require("./logger");
34
34
  const paths_1 = require("./paths");
35
35
  const secrets_1 = __importDefault(require("./secrets"));
36
- exports.default = { getSecrets, getCredentials, createCredentials, checkSecrets, getSecretsError };
36
+ exports.default = { getScopes, getSecrets, getCredentials, createCredentials, checkSecrets, getSecretsError, getScopesError };
37
37
  const callbackPort = 6006;
38
38
  const callbackURI = `http://localhost:${callbackPort}/oauthcallback`;
39
- const scope = ['https://www.googleapis.com/auth/youtube.readonly'];
40
- async function getSecrets(profile) {
39
+ function getScopes() {
40
+ const scopesFile = (0, paths_1.getScopesFile)();
41
+ const scopes = (0, jsonLib_1.getJSON)(scopesFile, () => (0, logger_1.error)(secrets_1.default.getScopesError(scopesFile)));
42
+ return scopes;
43
+ }
44
+ function getSecrets(profile) {
41
45
  const secretsFile = (0, paths_1.getSecretsFile)(profile);
42
46
  const secretsObject = (0, jsonLib_1.getJSON)(secretsFile, () => (0, logger_1.error)(secrets_1.default.getSecretsError(profile, secretsFile)));
43
47
  secrets_1.default.checkSecrets(profile, secretsObject, secretsFile);
@@ -50,6 +54,7 @@ async function getCredentials(profile, auth) {
50
54
  }
51
55
  exports.getCredentials = getCredentials;
52
56
  async function createCredentials(profile, auth) {
57
+ const scope = secrets_1.default.getScopes();
53
58
  return new Promise((resolve) => {
54
59
  const authUrl = auth.generateAuthUrl({
55
60
  // eslint-disable-next-line camelcase
@@ -57,7 +62,7 @@ async function createCredentials(profile, auth) {
57
62
  scope,
58
63
  });
59
64
  const server = http_1.default.createServer(async (request, response) => {
60
- response.end('<h1>Please close this page and return to application</h1>');
65
+ response.end('<h1>Please close this page and return to application. Wait for application to be finished automatically.</h1>');
61
66
  if (request.url) {
62
67
  const url = new URL(`http://${request.headers.host}${request.url}`);
63
68
  const code = url.searchParams.get('code');
@@ -79,16 +84,45 @@ function checkSecrets(profile, secretsObject, secretsFile) {
79
84
  }
80
85
  (0, logger_1.error)(`Error in credentials file: redirect URI should be ${callbackURI}.\n${secrets_1.default.getSecretsError(profile, secretsFile)}`);
81
86
  }
87
+ function getScopesError(scopesFile) {
88
+ return [
89
+ `File ${scopesFile} not found!`,
90
+ `This application had to have pre-defined file ${scopesFile} that will declare needed scopes`,
91
+ ].join('\n');
92
+ }
82
93
  function getSecretsError(profile, secretsFile) {
83
94
  return [
84
95
  `File ${secretsFile} not found!`,
85
- 'To obtain it, please create correct OAuth client ID:',
86
- '\tGo to https://console.cloud.google.com/apis/credentials/oauthclient',
87
- '\t[if applicable] Click "+ Create credentials" and choose "OAuth client ID',
88
- '\tSet application type "Web application"',
89
- `\tAdd authorized redirect URI: ${callbackURI}`,
90
- '\tClick "Create"',
91
- `\tClick "Download JSON" and download credentials to ./secrets/${profile}.json`,
96
+ 'Here is how to obtain it:',
97
+ '\tGo to https://console.cloud.google.com/projectcreate',
98
+ '\t\tChoose project name',
99
+ '\t\tClick "CREATE" and wait for project to become created',
100
+ '\tGo to https://console.cloud.google.com/apis/dashboard',
101
+ '\t\tSelect just created project in the top left dropdown list',
102
+ '\t\tClick "ENABLE APIS AND SERVICES"',
103
+ '\t\t\tClick API you need',
104
+ '\t\t\tClick "ENABLE" and wait for API to become enabled',
105
+ '\t\tClick "Credentials" tab on the left sidebar',
106
+ '\t\t\tClick "CONFIGURE CONSENT SCREEN" on the right',
107
+ '\t\t\t\tChoose "External"',
108
+ '\t\t\t\tClick "CREATE"',
109
+ '\t\t\t\tChoose app name, i.e. "NodeJS"',
110
+ '\t\t\t\tSpecify your email as user support email and as developer contact information on the very bottom',
111
+ '\t\t\t\tClick "Save and continue"',
112
+ '\t\t\tClick "Add or remove scopes"',
113
+ `\t\t\t\tAdd scopes: ${secrets_1.default.getScopes().join(',')}`,
114
+ '\t\t\t\tClick "Save and continue"',
115
+ '\t\t\tClick "Add users"',
116
+ '\t\t\t\tAdd your email',
117
+ '\t\t\t\tClick "Save and continue"',
118
+ '\t\t\tClick "Back to dashboard" on the very bottom',
119
+ '\t\tClick "Credentials" on the left sidebar',
120
+ '\t\t\tClick "CREATE CREDENTIALS" and choose "OAuth client ID"',
121
+ '\t\t\t\tSelect application type "Web application"',
122
+ '\t\t\t\tSpecify app name, i.e. "NodeJS"',
123
+ `\t\t\t\tAdd authorized redirect URI: ${callbackURI}`,
124
+ '\t\t\t\tClick "CREATE"',
125
+ `\t\t\t\tClick "DOWNLOAD JSON" and download credentials to ./secrets/${profile}.json`,
92
126
  'Then start this script again',
93
127
  ].join('\n');
94
128
  }
@@ -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": "2.1.3",
3
+ "version": "3.0.1",
4
4
  "description": "Provides quick interface for getting google API data",
5
5
  "keywords": [
6
6
  "google",
package/src/index.ts CHANGED
@@ -2,4 +2,4 @@ export * as calendar from './lib/api/calendar';
2
2
  export * as youtube from './lib/api/youtube';
3
3
  export { getItems } from './lib/api/shared';
4
4
  export { createProfile, getProfiles } from './lib/profiles';
5
- export { login } from './lib/auth';
5
+ export { login, getAuth } from './lib/auth';
@@ -1,5 +1,6 @@
1
1
  import { google } from 'googleapis';
2
2
  import type GoogleApis from 'googleapis';
3
+ import logger from '../logger';
3
4
  import profiles from '../profiles';
4
5
  import secrets from '../secrets';
5
6
 
@@ -10,12 +11,17 @@ jest.mock<typeof auth>('../auth', () => ({
10
11
  getAuth : jest.fn().mockImplementation(async () => googleAuth),
11
12
  }));
12
13
 
14
+ jest.mock<Partial<typeof logger>>('../logger', () => ({
15
+ info : jest.fn(),
16
+ warn : jest.fn(),
17
+ }));
18
+
13
19
  jest.mock<Partial<typeof profiles>>('../profiles', () => ({
14
20
  getProfiles : jest.fn().mockImplementation(() => allProfiles),
15
21
  }));
16
22
 
17
23
  jest.mock<Partial<typeof secrets>>('../secrets', () => ({
18
- getSecrets : jest.fn().mockImplementation(async () => secretsObject),
24
+ getSecrets : jest.fn().mockImplementation(() => secretsObject),
19
25
  getCredentials : jest.fn().mockImplementation(async () => credentials),
20
26
  }));
21
27
 
@@ -60,6 +66,15 @@ describe('src/lib/auth', () => {
60
66
  });
61
67
  });
62
68
 
69
+ it('should show auth progress for all profiles', async () => {
70
+ await original.login();
71
+
72
+ expect(logger.warn).toBeCalledWith('username1 - logging in...');
73
+ expect(logger.warn).toBeCalledWith('username2 - logging in...');
74
+ expect(logger.info).toBeCalledWith('username1 - logged in successfully');
75
+ expect(logger.info).toBeCalledWith('username2 - logged in successfully');
76
+ });
77
+
63
78
  it('should auth only specified profile', async () => {
64
79
  await original.login('username1');
65
80
 
@@ -7,6 +7,7 @@ jest.mock<typeof paths>('../paths', () => ({
7
7
  ensureDir : jest.fn().mockImplementation((dirPath) => dirPath),
8
8
  ensureFile : jest.fn().mockImplementation((filePath) => filePath),
9
9
  getProfilesFile : jest.fn().mockImplementation(() => profilesFile),
10
+ getScopesFile : jest.fn().mockImplementation(() => scopesFile),
10
11
  getSecretsFile : jest.fn().mockImplementation(() => secretsFile),
11
12
  getCredentialsFile : jest.fn().mockImplementation(() => credentialsFile),
12
13
  }));
@@ -27,6 +28,7 @@ const dirPath = 'dirPath';
27
28
  const filePath = 'parentDir/filePath';
28
29
 
29
30
  const profilesFile = 'input/profiles.json';
31
+ const scopesFile = 'scopes.json';
30
32
  const secretsFile = 'secrets/username.json';
31
33
  const credentialsFile = 'secrets/username.credentials.json';
32
34
 
@@ -98,6 +100,14 @@ describe('src/lib/paths', () => {
98
100
  });
99
101
  });
100
102
 
103
+ describe('getScopesFile', () => {
104
+ it('should return scopes file', () => {
105
+ const result = original.getScopesFile();
106
+
107
+ expect(result).toEqual(scopesFile);
108
+ });
109
+ });
110
+
101
111
  describe('getSecretsFile', () => {
102
112
  it('should return secrets file', () => {
103
113
  const result = original.getSecretsFile(profile);
@@ -9,10 +9,12 @@ import type { Secrets } from '../../types';
9
9
  import secrets from '../secrets';
10
10
  const original = jest.requireActual('../secrets').default as typeof secrets;
11
11
  jest.mock<typeof secrets>('../secrets', () => ({
12
- getSecrets : jest.fn(),
13
- getCredentials : jest.fn(),
12
+ getScopes : jest.fn().mockImplementation(() => scopesJSON),
13
+ getSecrets : jest.fn().mockImplementation(() => secretsJSON),
14
+ getCredentials : jest.fn(),
14
15
  createCredentials : jest.fn(),
15
- checkSecrets : jest.fn(),
16
+ checkSecrets : jest.fn(),
17
+ getScopesError : jest.fn().mockImplementation(() => scopesError),
16
18
  getSecretsError : jest.fn().mockImplementation(() => secretsError),
17
19
  }));
18
20
 
@@ -36,7 +38,7 @@ jest.mock<Partial<typeof colorette>>('colorette', () => ({
36
38
  }));
37
39
 
38
40
  jest.mock<Partial<typeof jsonLib>>('../jsonLib', () => ({
39
- getJSON : jest.fn().mockImplementation(() => json),
41
+ getJSON : jest.fn().mockImplementation(() => json),
40
42
  getJSONAsync : jest.fn().mockImplementation(async () => json),
41
43
  }));
42
44
 
@@ -48,22 +50,29 @@ jest.mock<Partial<typeof logger>>('../logger', () => ({
48
50
  }));
49
51
 
50
52
  const profile = 'username1';
53
+ const scopesFile = 'scopes.json';
51
54
  const secretsFile = 'secrets/username1.json';
52
55
  const credentialsFile = 'secrets/username1.credentials.json';
53
56
  const wrongRedirectURI = 'wrong_redirect_uri';
54
57
 
58
+ const scopesError = 'scopesError';
55
59
  const secretsError = 'secretsError';
56
60
 
61
+ const scopesJSON: string[] = [
62
+ 'https://www.googleapis.com/auth/calendar.calendars.readonly',
63
+ 'https://www.googleapis.com/auth/calendar.events.readonly',
64
+ ];
65
+
57
66
  const secretsJSON: Secrets = {
58
67
  web : {
59
68
  /* eslint-disable camelcase */
60
- client_id : 'client_id.apps.googleusercontent.com',
61
- project_id : 'project_id',
62
- auth_uri : 'https://accounts.google.com/o/oauth2/auth',
63
- token_uri : 'https://oauth2.googleapis.com/token',
69
+ client_id : 'client_id.apps.googleusercontent.com',
70
+ project_id : 'project_id',
71
+ auth_uri : 'https://accounts.google.com/o/oauth2/auth',
72
+ token_uri : 'https://oauth2.googleapis.com/token',
64
73
  auth_provider_x509_cert_url : 'https://www.googleapis.com/oauth2/v1/certs',
65
- client_secret : 'client_secret',
66
- redirect_uris : [ 'http://localhost:6006/oauthcallback' ],
74
+ client_secret : 'client_secret',
75
+ redirect_uris : [ 'http://localhost:6006/oauthcallback' ],
67
76
  /* eslint-enable camelcase */
68
77
  },
69
78
  };
@@ -78,7 +87,7 @@ const code = 'code';
78
87
  const authUrl = 'https://authUrl';
79
88
  const auth = {
80
89
  generateAuthUrl : jest.fn().mockReturnValue(authUrl),
81
- getToken : jest.fn().mockReturnValue({ tokens : credentialsJSON }),
90
+ getToken : jest.fn().mockReturnValue({ tokens : credentialsJSON }),
82
91
  } as unknown as GoogleApis.Common.OAuth2Client;
83
92
 
84
93
  let request: http.IncomingMessage;
@@ -100,6 +109,33 @@ const close = jest.fn().mockImplementation(() => {
100
109
  });
101
110
 
102
111
  describe('src/lib/secrets', () => {
112
+ describe('getScopes', () => {
113
+ const getJSONSpy = jest.spyOn(jsonLib, 'getJSON');
114
+
115
+ beforeEach(() => {
116
+ json = scopesJSON;
117
+ });
118
+
119
+ it('should get json from scopes file', async () => {
120
+ await original.getScopes();
121
+
122
+ expect(getJSONSpy).toBeCalled();
123
+ expect(getJSONSpy.mock.calls[0][0]).toEqual(scopesFile);
124
+ });
125
+
126
+ it('should fallback to error', async () => {
127
+ await original.getScopes();
128
+
129
+ expect(getJSONSpy.mock.calls[0][1]).toThrowError(scopesError);
130
+ });
131
+
132
+ it('should return scopes', async () => {
133
+ const result = await original.getScopes();
134
+
135
+ expect(result).toEqual(scopesJSON);
136
+ });
137
+ });
138
+
103
139
  describe('getSecrets', () => {
104
140
  const getJSONSpy = jest.spyOn(jsonLib, 'getJSON');
105
141
 
@@ -172,7 +208,7 @@ describe('src/lib/secrets', () => {
172
208
 
173
209
  beforeEach(() => {
174
210
  request = {
175
- url : `/request.url?code=${code}`,
211
+ url : `/request.url?code=${code}`,
176
212
  headers : {
177
213
  host : 'localhost:6006',
178
214
  },
@@ -187,7 +223,10 @@ describe('src/lib/secrets', () => {
187
223
  expect(auth.generateAuthUrl).toBeCalledWith({
188
224
  // eslint-disable-next-line camelcase
189
225
  access_type : 'offline',
190
- scope : [ 'https://www.googleapis.com/auth/youtube.readonly' ],
226
+ scope : [
227
+ 'https://www.googleapis.com/auth/calendar.calendars.readonly',
228
+ 'https://www.googleapis.com/auth/calendar.events.readonly',
229
+ ],
191
230
  });
192
231
  });
193
232
 
@@ -205,7 +244,7 @@ describe('src/lib/secrets', () => {
205
244
 
206
245
  await original.createCredentials(profile, auth);
207
246
 
208
- expect(logger.info).toBeCalledWith(`Please open yellow:https://authUrl in your browser using google profile for yellow:${profile} and allow access to yellow:https://www.googleapis.com/auth/youtube.readonly`);
247
+ expect(logger.info).toBeCalledWith(`Please open yellow:https://authUrl in your browser using google profile for yellow:${profile} and allow access to yellow:https://www.googleapis.com/auth/calendar.calendars.readonly,https://www.googleapis.com/auth/calendar.events.readonly`);
209
248
  });
210
249
 
211
250
  it('should ask to close webpage', async () => {
@@ -213,7 +252,7 @@ describe('src/lib/secrets', () => {
213
252
 
214
253
  await original.createCredentials(profile, auth);
215
254
 
216
- expect(response.end).toBeCalledWith('<h1>Please close this page and return to application</h1>');
255
+ expect(response.end).toBeCalledWith('<h1>Please close this page and return to application. Wait for application to be finished automatically.</h1>');
217
256
  });
218
257
 
219
258
  it('should close server if request.url is truthy', async () => {
@@ -287,17 +326,48 @@ describe('src/lib/secrets', () => {
287
326
  });
288
327
  });
289
328
 
329
+ describe('getScopesError', () => {
330
+ it('should return error message with instructions', () => {
331
+ const result = original.getScopesError(scopesFile);
332
+ expect(result).toEqual(`File ${scopesFile} not found!\n\
333
+ This application had to have pre-defined file ${scopesFile} that will declare needed scopes`);
334
+ });
335
+ });
336
+
290
337
  describe('getSecretsError', () => {
291
338
  it('should return error message with instructions', () => {
292
339
  const result = original.getSecretsError(profile, secretsFile);
293
340
  expect(result).toEqual(`File ${secretsFile} not found!\n\
294
- To obtain it, please create correct OAuth client ID:\n\
295
- \tGo to https://console.cloud.google.com/apis/credentials/oauthclient\n\
296
- \t[if applicable] Click "+ Create credentials" and choose "OAuth client ID\n\
297
- \tSet application type "Web application"\n\
298
- \tAdd authorized redirect URI: http://localhost:6006/oauthcallback\n\
299
- \tClick "Create"\n\
300
- \tClick "Download JSON" and download credentials to ./secrets/${profile}.json\n\
341
+ Here is how to obtain it:\n\
342
+ Go to https://console.cloud.google.com/projectcreate\n\
343
+ Choose project name\n\
344
+ Click "CREATE" and wait for project to become created\n\
345
+ Go to https://console.cloud.google.com/apis/dashboard\n\
346
+ Select just created project in the top left dropdown list\n\
347
+ Click "ENABLE APIS AND SERVICES"\n\
348
+ Click API you need\n\
349
+ Click "ENABLE" and wait for API to become enabled\n\
350
+ Click "Credentials" tab on the left sidebar\n\
351
+ Click "CONFIGURE CONSENT SCREEN" on the right\n\
352
+ Choose "External"\n\
353
+ Click "CREATE"\n\
354
+ Choose app name, i.e. "NodeJS"\n\
355
+ Specify your email as user support email and as developer contact information on the very bottom\n\
356
+ Click "Save and continue"\n\
357
+ Click "Add or remove scopes"\n\
358
+ Add scopes: https://www.googleapis.com/auth/calendar.calendars.readonly,https://www.googleapis.com/auth/calendar.events.readonly\n\
359
+ Click "Save and continue"\n\
360
+ Click "Add users"\n\
361
+ Add your email\n\
362
+ Click "Save and continue"\n\
363
+ Click "Back to dashboard" on the very bottom\n\
364
+ Click "Credentials" on the left sidebar\n\
365
+ Click "CREATE CREDENTIALS" and choose "OAuth client ID"\n\
366
+ Select application type "Web application"\n\
367
+ Specify app name, i.e. "NodeJS"\n\
368
+ Add authorized redirect URI: http://localhost:6006/oauthcallback\n\
369
+ Click "CREATE"\n\
370
+ Click "DOWNLOAD JSON" and download credentials to ./secrets/${profile}.json\n\
301
371
  Then start this script again`);
302
372
  });
303
373
  });
@@ -36,11 +36,11 @@ const googleAuth = {
36
36
  setCredentials : jest.fn(),
37
37
  };
38
38
 
39
- const calendars: Array<{ summary?: string, description?: string, hidden?: boolean }> = [
40
- { summary : 'calendar 1', description : 'calendar 1 description', hidden : false },
41
- { summary : 'calendar 2', description : 'calendar 2 description', hidden : undefined },
42
- { summary : 'calendar 3', description : undefined, hidden : true },
43
- { summary : 'calendar 4', description : undefined, hidden : undefined },
39
+ const calendars: Array<{ id?: string | null | undefined, summary?: string, description?: string, hidden?: boolean }> = [
40
+ { id : 'id1', summary : 'calendar 1', description : 'calendar 1 description', hidden : false },
41
+ { id : 'id2', summary : 'calendar 2', description : 'calendar 2 description', hidden : undefined },
42
+ { id : null, summary : 'calendar 3', description : undefined, hidden : true },
43
+ { id : 'id4', summary : 'calendar 4', description : undefined, hidden : undefined },
44
44
  ];
45
45
 
46
46
  const calendarsResponse = [
@@ -49,11 +49,11 @@ const calendarsResponse = [
49
49
  [ calendars[2], calendars[3] ],
50
50
  ];
51
51
 
52
- const events: Array<{ summary?: string, source?: { url?: string, title?: string} }> = [
53
- { summary : 'event 1', source : { title : 'source 1', url : 'https://example.com' } },
54
- { summary : 'event 2', source : { title : 'source 2', url : undefined } },
55
- { summary : 'event 3', source : { title : undefined, url : undefined } },
56
- { summary : 'event 4', source : undefined },
52
+ const events: Array<{ id?: string | null | undefined, summary?: string, source?: { url?: string, title?: string} }> = [
53
+ { id : 'id1', summary : 'event 1', source : { title : 'source 1', url : 'https://example.com' } },
54
+ { id : null, summary : 'event 2', source : { title : 'source 2', url : undefined } },
55
+ { id : 'id3', summary : 'event 3', source : { title : undefined, url : undefined } },
56
+ { id : 'id4', summary : 'event 4', source : undefined },
57
57
  ];
58
58
 
59
59
  const eventsResponse = [
@@ -36,11 +36,11 @@ const googleAuth = {
36
36
  setCredentials : jest.fn(),
37
37
  };
38
38
 
39
- const playlistItems: Array<{ snippet?: { title?: string, resourceId?: { videoId?: string } } }> = [
40
- { snippet : { title : 'video1', resourceId : { videoId : 'video1Id' } } },
41
- { snippet : { title : 'video2', resourceId : { videoId : undefined } } },
42
- { snippet : { title : undefined, resourceId : undefined } },
43
- { snippet : undefined },
39
+ const playlistItems: Array<{ id?: string | null | undefined, snippet?: { title?: string, resourceId?: { videoId?: string } } }> = [
40
+ { id : 'id1', snippet : { title : 'video1', resourceId : { videoId : 'video1Id' } } },
41
+ { id : null, snippet : { title : 'video2', resourceId : { videoId : undefined } } },
42
+ { id : 'id3', snippet : { title : undefined, resourceId : undefined } },
43
+ { id : 'id4', snippet : undefined },
44
44
  ];
45
45
 
46
46
  const playlistItemsResponse = [
package/src/lib/auth.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { google } from 'googleapis';
2
2
  import type GoogleApis from 'googleapis';
3
+ import { info, warn } from './logger';
3
4
  import { getProfiles } from './profiles';
4
5
  import { getCredentials, getSecrets } from './secrets';
5
6
 
@@ -12,12 +13,14 @@ async function login(profile?: string): Promise<void> {
12
13
  const profiles = getProfiles().filter((p) => !profile || p === profile);
13
14
 
14
15
  for (const profile of profiles) {
16
+ warn(`${profile} - logging in...`);
15
17
  await auth.getAuth(profile);
18
+ info(`${profile} - logged in successfully`);
16
19
  }
17
20
  }
18
21
 
19
22
  async function getAuth(profile: string): Promise<GoogleApis.Common.OAuth2Client> {
20
- const secrets = await getSecrets(profile);
23
+ const secrets = getSecrets(profile);
21
24
 
22
25
  const googleAuth = new google.auth.OAuth2(
23
26
  secrets.web.client_id,
package/src/lib/paths.ts CHANGED
@@ -2,8 +2,8 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import paths from './paths';
4
4
 
5
- export { ensureDir, ensureFile, getProfilesFile, getSecretsFile, getCredentialsFile };
6
- export default { ensureDir, ensureFile, getProfilesFile, getSecretsFile, getCredentialsFile };
5
+ export { ensureDir, ensureFile, getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile };
6
+ export default { ensureDir, ensureFile, getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile };
7
7
 
8
8
  const dirPaths = {
9
9
  input : 'input',
@@ -30,6 +30,10 @@ function getProfilesFile() {
30
30
  return path.join(dirPaths.input, 'profiles.json');
31
31
  }
32
32
 
33
+ function getScopesFile() {
34
+ return path.join(dirPaths.input, 'scopes.json');
35
+ }
36
+
33
37
  function getSecretsFile(profile: string) {
34
38
  return path.join(dirPaths.secrets, `${profile}.json`);
35
39
  }
@@ -4,18 +4,23 @@ import type GoogleApis from 'googleapis';
4
4
  import type { Secrets } from '../types';
5
5
  import { getJSON, getJSONAsync } from './jsonLib';
6
6
  import { info, error } from './logger';
7
- import { getSecretsFile, getCredentialsFile } from './paths';
7
+ import { getScopesFile, getSecretsFile, getCredentialsFile } from './paths';
8
8
 
9
9
  import secrets from './secrets';
10
10
 
11
11
  export { getSecrets, getCredentials };
12
- export default { getSecrets, getCredentials, createCredentials, checkSecrets, getSecretsError };
12
+ export default { getScopes, getSecrets, getCredentials, createCredentials, checkSecrets, getSecretsError, getScopesError };
13
13
 
14
14
  const callbackPort = 6006;
15
15
  const callbackURI = `http://localhost:${callbackPort}/oauthcallback`;
16
- const scope = [ 'https://www.googleapis.com/auth/youtube.readonly' ];
17
16
 
18
- async function getSecrets(profile: string): Promise<Secrets> {
17
+ function getScopes(): string[] {
18
+ const scopesFile = getScopesFile();
19
+ const scopes = getJSON<string[]>(scopesFile, () => error(secrets.getScopesError(scopesFile)) as never);
20
+ return scopes;
21
+ }
22
+
23
+ function getSecrets(profile: string): Secrets {
19
24
  const secretsFile = getSecretsFile(profile);
20
25
  const secretsObject = getJSON<Secrets>(secretsFile, () => error(secrets.getSecretsError(profile, secretsFile)) as never);
21
26
  secrets.checkSecrets(profile, secretsObject, secretsFile);
@@ -28,6 +33,8 @@ async function getCredentials(profile: string, auth: GoogleApis.Common.OAuth2Cli
28
33
  }
29
34
 
30
35
  async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client): Promise<GoogleApis.Auth.Credentials> {
36
+ const scope = secrets.getScopes();
37
+
31
38
  return new Promise((resolve) => {
32
39
  const authUrl = auth.generateAuthUrl({
33
40
  // eslint-disable-next-line camelcase
@@ -36,7 +43,7 @@ async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Cl
36
43
  });
37
44
 
38
45
  const server = http.createServer(async (request, response) => {
39
- response.end('<h1>Please close this page and return to application</h1>');
46
+ response.end('<h1>Please close this page and return to application. Wait for application to be finished automatically.</h1>');
40
47
 
41
48
  if (request.url) {
42
49
  const url = new URL(`http://${request.headers.host}${request.url}`);
@@ -64,16 +71,46 @@ function checkSecrets(profile: string, secretsObject: Secrets, secretsFile: stri
64
71
  error(`Error in credentials file: redirect URI should be ${callbackURI}.\n${secrets.getSecretsError(profile, secretsFile)}`);
65
72
  }
66
73
 
74
+ function getScopesError(scopesFile: string) {
75
+ return [
76
+ `File ${scopesFile} not found!`,
77
+ `This application had to have pre-defined file ${scopesFile} that will declare needed scopes`,
78
+ ].join('\n');
79
+ }
80
+
67
81
  function getSecretsError(profile: string, secretsFile: string) {
68
82
  return [
69
83
  `File ${secretsFile} not found!`,
70
- 'To obtain it, please create correct OAuth client ID:',
71
- '\tGo to https://console.cloud.google.com/apis/credentials/oauthclient',
72
- '\t[if applicable] Click "+ Create credentials" and choose "OAuth client ID',
73
- '\tSet application type "Web application"',
74
- `\tAdd authorized redirect URI: ${callbackURI}`,
75
- '\tClick "Create"',
76
- `\tClick "Download JSON" and download credentials to ./secrets/${profile}.json`,
84
+ 'Here is how to obtain it:',
85
+ '\tGo to https://console.cloud.google.com/projectcreate',
86
+ '\t\tChoose project name',
87
+ '\t\tClick "CREATE" and wait for project to become created',
88
+ '\tGo to https://console.cloud.google.com/apis/dashboard',
89
+ '\t\tSelect just created project in the top left dropdown list',
90
+ '\t\tClick "ENABLE APIS AND SERVICES"',
91
+ '\t\t\tClick API you need',
92
+ '\t\t\tClick "ENABLE" and wait for API to become enabled',
93
+ '\t\tClick "Credentials" tab on the left sidebar',
94
+ '\t\t\tClick "CONFIGURE CONSENT SCREEN" on the right',
95
+ '\t\t\t\tChoose "External"',
96
+ '\t\t\t\tClick "CREATE"',
97
+ '\t\t\t\tChoose app name, i.e. "NodeJS"',
98
+ '\t\t\t\tSpecify your email as user support email and as developer contact information on the very bottom',
99
+ '\t\t\t\tClick "Save and continue"',
100
+ '\t\t\tClick "Add or remove scopes"',
101
+ `\t\t\t\tAdd scopes: ${secrets.getScopes().join(',')}`,
102
+ '\t\t\t\tClick "Save and continue"',
103
+ '\t\t\tClick "Add users"',
104
+ '\t\t\t\tAdd your email',
105
+ '\t\t\t\tClick "Save and continue"',
106
+ '\t\t\tClick "Back to dashboard" on the very bottom',
107
+ '\t\tClick "Credentials" on the left sidebar',
108
+ '\t\t\tClick "CREATE CREDENTIALS" and choose "OAuth client ID"',
109
+ '\t\t\t\tSelect application type "Web application"',
110
+ '\t\t\t\tSpecify app name, i.e. "NodeJS"',
111
+ `\t\t\t\tAdd authorized redirect URI: ${callbackURI}`,
112
+ '\t\t\t\tClick "CREATE"',
113
+ `\t\t\t\tClick "DOWNLOAD JSON" and download credentials to ./secrets/${profile}.json`,
77
114
  'Then start this script again',
78
115
  ].join('\n');
79
116
  }