@anmiles/google-api-wrapper 1.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/.eslintrc.js +91 -0
- package/.gitlab-ci.yml +118 -0
- package/.vscode/settings.json +10 -0
- package/CHANGELOG.md +14 -0
- package/LICENSE.md +21 -0
- package/README.md +35 -0
- package/coverage.config.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth.d.ts +9 -0
- package/dist/lib/auth.js +27 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/data.d.ts +23 -0
- package/dist/lib/data.js +45 -0
- package/dist/lib/data.js.map +1 -0
- package/dist/lib/jsonLib.d.ts +14 -0
- package/dist/lib/jsonLib.js +48 -0
- package/dist/lib/jsonLib.js.map +1 -0
- package/dist/lib/logger.d.ts +12 -0
- package/dist/lib/logger.js +46 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/paths.d.ts +14 -0
- package/dist/lib/paths.js +42 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/profiles.d.ts +10 -0
- package/dist/lib/profiles.js +34 -0
- package/dist/lib/profiles.js.map +1 -0
- package/dist/lib/secrets.d.ts +16 -0
- package/dist/lib/secrets.js +95 -0
- package/dist/lib/secrets.js.map +1 -0
- package/dist/lib/sleep.d.ts +6 -0
- package/dist/lib/sleep.js +11 -0
- package/dist/lib/sleep.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/secrets.d.ts +11 -0
- package/dist/types/secrets.js +3 -0
- package/dist/types/secrets.js.map +1 -0
- package/jest.config.js +22 -0
- package/package.json +50 -0
- package/src/index.ts +3 -0
- package/src/lib/__tests__/auth.test.ts +97 -0
- package/src/lib/__tests__/data.test.ts +154 -0
- package/src/lib/__tests__/jsonLib.test.ts +165 -0
- package/src/lib/__tests__/logger.test.ts +57 -0
- package/src/lib/__tests__/paths.test.ts +116 -0
- package/src/lib/__tests__/profiles.test.ts +117 -0
- package/src/lib/__tests__/secrets.test.ts +304 -0
- package/src/lib/__tests__/sleep.test.ts +17 -0
- package/src/lib/auth.ts +31 -0
- package/src/lib/data.ts +81 -0
- package/src/lib/jsonLib.ts +48 -0
- package/src/lib/logger.ts +21 -0
- package/src/lib/paths.ts +39 -0
- package/src/lib/profiles.ts +33 -0
- package/src/lib/secrets.ts +79 -0
- package/src/lib/sleep.ts +8 -0
- package/src/types/index.ts +1 -0
- package/src/types/secrets.ts +11 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createProfile = exports.setProfiles = exports.getProfiles = void 0;
|
|
7
|
+
const jsonLib_1 = require("./jsonLib");
|
|
8
|
+
const logger_1 = require("./logger");
|
|
9
|
+
const paths_1 = require("./paths");
|
|
10
|
+
const profiles_1 = __importDefault(require("./profiles"));
|
|
11
|
+
exports.default = { getProfiles, setProfiles, createProfile };
|
|
12
|
+
function getProfiles() {
|
|
13
|
+
const profilesFile = (0, paths_1.getProfilesFile)();
|
|
14
|
+
return (0, jsonLib_1.getJSON)(profilesFile, () => []);
|
|
15
|
+
}
|
|
16
|
+
exports.getProfiles = getProfiles;
|
|
17
|
+
function setProfiles(profiles) {
|
|
18
|
+
const profilesFile = (0, paths_1.getProfilesFile)();
|
|
19
|
+
(0, jsonLib_1.writeJSON)(profilesFile, profiles);
|
|
20
|
+
}
|
|
21
|
+
exports.setProfiles = setProfiles;
|
|
22
|
+
function createProfile(profile) {
|
|
23
|
+
if (!profile) {
|
|
24
|
+
(0, logger_1.error)('Usage: `npm run create <profile>` where `profile` - is any profile name you want');
|
|
25
|
+
}
|
|
26
|
+
const existingProfiles = profiles_1.default.getProfiles();
|
|
27
|
+
if (existingProfiles.includes(profile)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
existingProfiles.push(profile);
|
|
31
|
+
profiles_1.default.setProfiles(existingProfiles);
|
|
32
|
+
}
|
|
33
|
+
exports.createProfile = createProfile;
|
|
34
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/lib/profiles.ts"],"names":[],"mappings":";;;;;;AAAA,uCAA+C;AAC/C,qCAAiC;AACjC,mCAA0C;AAE1C,0DAAkC;AAGlC,kBAAe,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;AAE3D,SAAS,WAAW;IACnB,MAAM,YAAY,GAAG,IAAA,uBAAe,GAAE,CAAC;IACvC,OAAO,IAAA,iBAAO,EAAC,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;AACxC,CAAC;AANQ,kCAAW;AAQpB,SAAS,WAAW,CAAC,QAAkB;IACtC,MAAM,YAAY,GAAG,IAAA,uBAAe,GAAE,CAAC;IACvC,IAAA,mBAAS,EAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAXqB,kCAAW;AAajC,SAAS,aAAa,CAAC,OAAe;IACrC,IAAI,CAAC,OAAO,EAAE;QACb,IAAA,cAAK,EAAC,kFAAkF,CAAC,CAAC;KAC1F;IAED,MAAM,gBAAgB,GAAG,kBAAQ,CAAC,WAAW,EAAE,CAAC;IAEhD,IAAI,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;QACvC,OAAO;KACP;IAED,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,kBAAQ,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC;AACxC,CAAC;AA1BkC,sCAAa"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type GoogleApis from 'googleapis';
|
|
2
|
+
import type { Secrets } from '../types';
|
|
3
|
+
export { getSecrets, getCredentials };
|
|
4
|
+
declare const _default: {
|
|
5
|
+
getSecrets: typeof getSecrets;
|
|
6
|
+
getCredentials: typeof getCredentials;
|
|
7
|
+
createCredentials: typeof createCredentials;
|
|
8
|
+
checkSecrets: typeof checkSecrets;
|
|
9
|
+
getSecretsError: typeof getSecretsError;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
12
|
+
declare function getSecrets(profile: string): Promise<Secrets>;
|
|
13
|
+
declare function getCredentials(profile: string, auth: GoogleApis.Common.OAuth2Client): Promise<GoogleApis.Auth.Credentials>;
|
|
14
|
+
declare function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client): Promise<GoogleApis.Auth.Credentials>;
|
|
15
|
+
declare function checkSecrets(profile: string, secretsObject: Secrets, secretsFile: string): true | void;
|
|
16
|
+
declare function getSecretsError(profile: string, secretsFile: string): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.getCredentials = exports.getSecrets = void 0;
|
|
30
|
+
const http_1 = __importDefault(require("http"));
|
|
31
|
+
const colorette = __importStar(require("colorette"));
|
|
32
|
+
const jsonLib_1 = require("./jsonLib");
|
|
33
|
+
const logger_1 = require("./logger");
|
|
34
|
+
const paths_1 = require("./paths");
|
|
35
|
+
const secrets_1 = __importDefault(require("./secrets"));
|
|
36
|
+
exports.default = { getSecrets, getCredentials, createCredentials, checkSecrets, getSecretsError };
|
|
37
|
+
const callbackPort = 6006;
|
|
38
|
+
const callbackURI = `http://localhost:${callbackPort}/oauthcallback`;
|
|
39
|
+
const scope = ['https://www.googleapis.com/auth/youtube.readonly'];
|
|
40
|
+
async function getSecrets(profile) {
|
|
41
|
+
const secretsFile = (0, paths_1.getSecretsFile)(profile);
|
|
42
|
+
const secretsObject = (0, jsonLib_1.getJSON)(secretsFile, () => (0, logger_1.error)(secrets_1.default.getSecretsError(profile, secretsFile)));
|
|
43
|
+
secrets_1.default.checkSecrets(profile, secretsObject, secretsFile);
|
|
44
|
+
return secretsObject;
|
|
45
|
+
}
|
|
46
|
+
exports.getSecrets = getSecrets;
|
|
47
|
+
async function getCredentials(profile, auth) {
|
|
48
|
+
const credentialsFile = (0, paths_1.getCredentialsFile)(profile);
|
|
49
|
+
return (0, jsonLib_1.getJSONAsync)(credentialsFile, () => secrets_1.default.createCredentials(profile, auth));
|
|
50
|
+
}
|
|
51
|
+
exports.getCredentials = getCredentials;
|
|
52
|
+
async function createCredentials(profile, auth) {
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
const authUrl = auth.generateAuthUrl({
|
|
55
|
+
// eslint-disable-next-line camelcase
|
|
56
|
+
access_type: 'offline',
|
|
57
|
+
scope,
|
|
58
|
+
});
|
|
59
|
+
const server = http_1.default.createServer(async (request, response) => {
|
|
60
|
+
response.end('<h1>Please close this page and return to application</h1>');
|
|
61
|
+
if (request.url) {
|
|
62
|
+
const url = new URL(`http://${request.headers.host}${request.url}`);
|
|
63
|
+
const code = url.searchParams.get('code');
|
|
64
|
+
if (!code) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
server.close();
|
|
68
|
+
const { tokens } = await auth.getToken(code);
|
|
69
|
+
resolve(tokens);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
server.listen(callbackPort);
|
|
73
|
+
(0, logger_1.info)(`Please open ${colorette.yellow(authUrl)} in your browser using google profile for ${colorette.yellow(profile)} and allow access to ${colorette.yellow(scope.join(','))}`);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function checkSecrets(profile, secretsObject, secretsFile) {
|
|
77
|
+
if (secretsObject.web.redirect_uris[0] === callbackURI) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
(0, logger_1.error)(`Error in credentials file: redirect URI should be ${callbackURI}.\n${secrets_1.default.getSecretsError(profile, secretsFile)}`);
|
|
81
|
+
}
|
|
82
|
+
function getSecretsError(profile, secretsFile) {
|
|
83
|
+
return [
|
|
84
|
+
`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`,
|
|
92
|
+
'Then start this script again',
|
|
93
|
+
].join('\n');
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sleep = void 0;
|
|
4
|
+
exports.default = { sleep };
|
|
5
|
+
async function sleep(milliSeconds) {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
setTimeout(resolve, milliSeconds);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
exports.sleep = sleep;
|
|
11
|
+
//# sourceMappingURL=sleep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep.js","sourceRoot":"","sources":["../../src/lib/sleep.ts"],"names":[],"mappings":";;;AACA,kBAAe,EAAE,KAAK,EAAE,CAAC;AAEzB,KAAK,UAAU,KAAK,CAAC,YAAoB;IACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACJ,CAAC;AAPQ,sBAAK"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './secrets';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./secrets"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface Secrets {
|
|
2
|
+
web: {
|
|
3
|
+
client_id: `${string}.apps.googleusercontent.com`;
|
|
4
|
+
project_id: string;
|
|
5
|
+
auth_uri: 'https://accounts.google.com/o/oauth2/auth';
|
|
6
|
+
token_uri: 'https://oauth2.googleapis.com/token';
|
|
7
|
+
auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs';
|
|
8
|
+
client_secret: string;
|
|
9
|
+
redirect_uris: string[];
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/types/secrets.ts"],"names":[],"mappings":""}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
preset : 'ts-jest',
|
|
3
|
+
clearMocks : true,
|
|
4
|
+
|
|
5
|
+
roots : [ '<rootDir>/src' ],
|
|
6
|
+
testMatch : [ '<rootDir>/src/**/__tests__/*.test.ts' ],
|
|
7
|
+
transform : {
|
|
8
|
+
'^.+\\.ts$' : 'ts-jest',
|
|
9
|
+
},
|
|
10
|
+
collectCoverageFrom : [
|
|
11
|
+
'<rootDir>/src/**/*.ts',
|
|
12
|
+
'!<rootDir>/src/**/*.d.ts',
|
|
13
|
+
'!<rootDir>/src/*.ts',
|
|
14
|
+
'!<rootDir>/src/types/*.ts',
|
|
15
|
+
|
|
16
|
+
'!**/node_modules/**',
|
|
17
|
+
'!**/__tests__/**',
|
|
18
|
+
|
|
19
|
+
'!<rootDir>/coverage/**',
|
|
20
|
+
'!<rootDir>/dist/**',
|
|
21
|
+
],
|
|
22
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anmiles/google-api-wrapper",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Provides quick interface for getting google API data",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"google",
|
|
7
|
+
"api",
|
|
8
|
+
"auth"
|
|
9
|
+
],
|
|
10
|
+
"author": "Anatoliy Oblaukhov",
|
|
11
|
+
"homepage": "https://gitlab.com/anmiles/google-api-wrapper",
|
|
12
|
+
"repository": "gitlab:anmiles/google-api-wrapper",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18.14.2"
|
|
16
|
+
},
|
|
17
|
+
"main": "dist/index.js",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "rimraf dist && tsc",
|
|
20
|
+
"lint": "eslint --ext .js,.ts .",
|
|
21
|
+
"lint:fix": "npm run lint -- --fix",
|
|
22
|
+
"test": "jest --verbose",
|
|
23
|
+
"test:coverage": "npm test -- --coverage",
|
|
24
|
+
"test:ci": "npm test -- --ci --coverage",
|
|
25
|
+
"test:watch": "npm test -- --watch",
|
|
26
|
+
"test:watch:coverage": "npm test -- --watch --coverage",
|
|
27
|
+
"test:report:coverage": "nyc report --nycrc-path ./coverage.config.js -t ./coverage --report-dir ./coverage",
|
|
28
|
+
"create": "node ./dist/create.js",
|
|
29
|
+
"login": "node ./dist/login.js"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"colorette": "^2.0.19",
|
|
33
|
+
"execa": "^5.1.1",
|
|
34
|
+
"googleapis": "^104.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/jest": "^28.1.3",
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
|
39
|
+
"@typescript-eslint/parser": "^5.30.0",
|
|
40
|
+
"eslint": "^8.18.0",
|
|
41
|
+
"eslint-plugin-align-assignments": "^1.1.2",
|
|
42
|
+
"eslint-plugin-import": "^2.26.0",
|
|
43
|
+
"eslint-plugin-jest": "^26.5.3",
|
|
44
|
+
"jest": "^28.1.2",
|
|
45
|
+
"nyc": "^15.1.0",
|
|
46
|
+
"rimraf": "^3.0.2",
|
|
47
|
+
"ts-jest": "^28.0.5",
|
|
48
|
+
"typescript": "^4.7.4"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
import type GoogleApis from 'googleapis';
|
|
3
|
+
import profiles from '../profiles';
|
|
4
|
+
import secrets from '../secrets';
|
|
5
|
+
|
|
6
|
+
import auth from '../auth';
|
|
7
|
+
const original = jest.requireActual('../auth').default as typeof auth;
|
|
8
|
+
jest.mock<typeof auth>('../auth', () => ({
|
|
9
|
+
login : jest.fn(),
|
|
10
|
+
getAuth : jest.fn().mockImplementation(async () => googleAuth),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.mock<Partial<typeof profiles>>('../profiles', () => ({
|
|
14
|
+
getProfiles : jest.fn().mockImplementation(() => allProfiles),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
jest.mock<Partial<typeof secrets>>('../secrets', () => ({
|
|
18
|
+
getSecrets : jest.fn().mockImplementation(async () => secretsObject),
|
|
19
|
+
getCredentials : jest.fn().mockImplementation(async () => credentials),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock('googleapis', () => ({
|
|
23
|
+
google : {
|
|
24
|
+
auth : {
|
|
25
|
+
OAuth2 : jest.fn().mockImplementation(() => googleAuth),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
const profile = 'username';
|
|
31
|
+
const allProfiles = [ 'username1', 'username2' ];
|
|
32
|
+
const credentials = 'credentials' as GoogleApis.Auth.Credentials;
|
|
33
|
+
|
|
34
|
+
const googleAuth = {
|
|
35
|
+
setCredentials : jest.fn(),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const secretsObject = {
|
|
39
|
+
web : {
|
|
40
|
+
/* eslint-disable camelcase */
|
|
41
|
+
client_id : 'client_id',
|
|
42
|
+
client_secret : 'client_secret',
|
|
43
|
+
redirect_uris : [ 'redirect_uri' ],
|
|
44
|
+
/* eslint-enable camelcase */
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe('src/lib/auth', () => {
|
|
49
|
+
describe('login', () => {
|
|
50
|
+
it('should get profiles', async () => {
|
|
51
|
+
await original.login();
|
|
52
|
+
expect(profiles.getProfiles).toBeCalledWith();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should auth all profiles', async () => {
|
|
56
|
+
await original.login();
|
|
57
|
+
|
|
58
|
+
allProfiles.forEach((profile) => {
|
|
59
|
+
expect(auth.getAuth).toBeCalledWith(profile);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should auth only specified profile', async () => {
|
|
64
|
+
await original.login('username1');
|
|
65
|
+
|
|
66
|
+
expect(auth.getAuth).toBeCalledWith('username1');
|
|
67
|
+
expect(auth.getAuth).not.toBeCalledWith('username2');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('getAuth', () => {
|
|
72
|
+
it('should get secrets', async () => {
|
|
73
|
+
await original.getAuth(profile);
|
|
74
|
+
expect(secrets.getSecrets).toBeCalledWith(profile);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should get credentials', async () => {
|
|
78
|
+
await original.getAuth(profile);
|
|
79
|
+
expect(secrets.getCredentials).toBeCalledWith(profile, googleAuth);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should create OAuth2 instance', async () => {
|
|
83
|
+
await original.getAuth(profile);
|
|
84
|
+
expect(google.auth.OAuth2).toBeCalledWith(secretsObject.web.client_id, secretsObject.web.client_secret, secretsObject.web.redirect_uris[0]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should set credentials', async () => {
|
|
88
|
+
await original.getAuth(profile);
|
|
89
|
+
expect(googleAuth.setCredentials).toBeCalledWith(credentials);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should return google auth', async () => {
|
|
93
|
+
const result = await original.getAuth(profile);
|
|
94
|
+
expect(result).toEqual(googleAuth);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import auth from '../auth';
|
|
3
|
+
import data from '../data';
|
|
4
|
+
import logger from '../logger';
|
|
5
|
+
import sleep from '../sleep';
|
|
6
|
+
|
|
7
|
+
const original = jest.requireActual('../data').default as typeof data;
|
|
8
|
+
jest.mock<Partial<typeof data>>('../data', () => ({
|
|
9
|
+
getItems : jest.fn().mockImplementation(async () => videosList),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
jest.mock<Partial<typeof fs>>('fs', () => ({
|
|
13
|
+
writeFileSync : jest.fn(),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('googleapis', () => ({
|
|
17
|
+
google : {
|
|
18
|
+
calendar : jest.fn().mockImplementation(() => ({ events : eventsAPI })),
|
|
19
|
+
youtube : jest.fn().mockImplementation(() => ({ playlistItems : videosAPI })),
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock<Partial<typeof auth>>('../auth', () => ({
|
|
24
|
+
getAuth : jest.fn().mockImplementation(() => googleAuth),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.mock<Partial<typeof logger>>('../logger', () => ({
|
|
28
|
+
log : jest.fn(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
jest.mock<Partial<typeof sleep>>('../sleep', () => ({
|
|
32
|
+
sleep : jest.fn(),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
const profile = 'username';
|
|
36
|
+
|
|
37
|
+
const googleAuth = {
|
|
38
|
+
setCredentials : jest.fn(),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const videosList: Array<{ snippet?: { title?: string, resourceId?: { videoId?: string } } }> = [
|
|
42
|
+
{ snippet : { title : 'video1', resourceId : { videoId : 'video1Id' } } },
|
|
43
|
+
{ snippet : { title : 'video2', resourceId : { videoId : undefined } } },
|
|
44
|
+
{ snippet : { title : undefined, resourceId : undefined } },
|
|
45
|
+
{ snippet : undefined },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const videos = [
|
|
49
|
+
[ videosList[0], videosList[1] ],
|
|
50
|
+
null,
|
|
51
|
+
[ videosList[2], videosList[3] ],
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const eventsList: Array<{ snippet?: { title?: string, resourceId?: { videoId?: string } } }> = [
|
|
55
|
+
{ snippet : { title : 'video1', resourceId : { videoId : 'video1Id' } } },
|
|
56
|
+
{ snippet : { title : 'video2', resourceId : { videoId : undefined } } },
|
|
57
|
+
{ snippet : { title : undefined, resourceId : undefined } },
|
|
58
|
+
{ snippet : undefined },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const events = [
|
|
62
|
+
[ eventsList[0], eventsList[1] ],
|
|
63
|
+
null,
|
|
64
|
+
[ eventsList[2], eventsList[3] ],
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const pageTokens = [
|
|
68
|
+
undefined,
|
|
69
|
+
'token1',
|
|
70
|
+
'token2',
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const getAPI = <T>(items: Array<Array<T> | null>) => ({
|
|
74
|
+
list : jest.fn().mockImplementation(async ({ pageToken }: {pageToken?: string}) => {
|
|
75
|
+
const index = pageTokens.indexOf(pageToken);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
data : {
|
|
79
|
+
items : items[index],
|
|
80
|
+
nextPageToken : pageTokens[index + 1],
|
|
81
|
+
pageInfo : !items[index] ? null : {
|
|
82
|
+
totalResults : items.reduce((sum, list) => sum + (list?.length || 0), 0),
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const eventsAPI = getAPI(events);
|
|
90
|
+
const videosAPI = getAPI(videos);
|
|
91
|
+
|
|
92
|
+
const args = { playlistId : 'LL', part : [ 'snippet' ], maxResults : 50 };
|
|
93
|
+
|
|
94
|
+
describe('src/lib/data', () => {
|
|
95
|
+
describe('getItems', () => {
|
|
96
|
+
it('should call API list method for each page', async () => {
|
|
97
|
+
await original.getItems(videosAPI, args);
|
|
98
|
+
|
|
99
|
+
pageTokens.forEach((pageToken) => {
|
|
100
|
+
expect(videosAPI.list).toBeCalledWith({ ...args, pageToken });
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should output progress', async () => {
|
|
105
|
+
await original.getItems(videosAPI, args);
|
|
106
|
+
|
|
107
|
+
expect(logger.log).toBeCalledTimes(videos.length);
|
|
108
|
+
expect(logger.log).toBeCalledWith('Getting items (2 of 4)...');
|
|
109
|
+
expect(logger.log).toBeCalledWith('Getting items (2 of many)...');
|
|
110
|
+
expect(logger.log).toBeCalledWith('Getting items (4 of 4)...');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should sleep after reach request', async () => {
|
|
114
|
+
await original.getItems(videosAPI, args);
|
|
115
|
+
|
|
116
|
+
expect(sleep.sleep).toBeCalledTimes(videos.length);
|
|
117
|
+
expect(sleep.sleep).toBeCalledWith(300);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should return items data', async () => {
|
|
121
|
+
const items = await original.getItems(videosAPI, args);
|
|
122
|
+
|
|
123
|
+
expect(items).toEqual(videosList);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('getEvents', () => {
|
|
128
|
+
it('should call getItems', async () => {
|
|
129
|
+
await original.getEvents(profile, args);
|
|
130
|
+
|
|
131
|
+
expect(data.getItems).toBeCalledWith(eventsAPI, args);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should return events', async () => {
|
|
135
|
+
const events = await original.getEvents(profile, args);
|
|
136
|
+
|
|
137
|
+
expect(events).toEqual(eventsList);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('getVideos', () => {
|
|
142
|
+
it('should call getItems', async () => {
|
|
143
|
+
await original.getVideos(profile, args);
|
|
144
|
+
|
|
145
|
+
expect(data.getItems).toBeCalledWith(videosAPI, args);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should return videos', async () => {
|
|
149
|
+
const videos = await original.getVideos(profile, args);
|
|
150
|
+
|
|
151
|
+
expect(videos).toEqual(videosList);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|