@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 +9 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/auth.js +4 -1
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/paths.d.ts +3 -1
- package/dist/lib/paths.js +6 -2
- package/dist/lib/paths.js.map +1 -1
- package/dist/lib/secrets.d.ts +5 -1
- package/dist/lib/secrets.js +45 -11
- package/dist/lib/secrets.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/lib/__tests__/auth.test.ts +16 -1
- package/src/lib/__tests__/paths.test.ts +10 -0
- package/src/lib/__tests__/secrets.test.ts +92 -22
- package/src/lib/api/__tests__/calendar.test.ts +10 -10
- package/src/lib/api/__tests__/youtube.test.ts +5 -5
- package/src/lib/auth.ts +4 -1
- package/src/lib/paths.ts +6 -2
- package/src/lib/secrets.ts +49 -12
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,
|
|
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 =
|
|
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);
|
package/dist/lib/auth.js.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/lib/paths.js.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/lib/secrets.d.ts
CHANGED
|
@@ -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
|
|
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;
|
package/dist/lib/secrets.js
CHANGED
|
@@ -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
|
-
|
|
40
|
-
|
|
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
|
|
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
|
-
'
|
|
86
|
-
'\tGo to https://console.cloud.google.com/
|
|
87
|
-
'\t
|
|
88
|
-
'\
|
|
89
|
-
|
|
90
|
-
'\
|
|
91
|
-
|
|
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
|
}
|
package/dist/lib/secrets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAwB;AACxB,qDAAuC;AAGvC,uCAAkD;AAClD,qCAAuC;AACvC,
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
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(
|
|
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
|
-
|
|
13
|
-
|
|
12
|
+
getScopes : jest.fn().mockImplementation(() => scopesJSON),
|
|
13
|
+
getSecrets : jest.fn().mockImplementation(() => secretsJSON),
|
|
14
|
+
getCredentials : jest.fn(),
|
|
14
15
|
createCredentials : jest.fn(),
|
|
15
|
-
checkSecrets
|
|
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
|
|
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
|
|
61
|
-
project_id
|
|
62
|
-
auth_uri
|
|
63
|
-
token_uri
|
|
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
|
|
66
|
-
redirect_uris
|
|
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
|
|
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
|
|
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
|
|
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/
|
|
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
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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 =
|
|
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
|
}
|
package/src/lib/secrets.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
'
|
|
71
|
-
'\tGo to https://console.cloud.google.com/
|
|
72
|
-
'\t
|
|
73
|
-
'\
|
|
74
|
-
|
|
75
|
-
'\
|
|
76
|
-
|
|
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
|
}
|