@anmiles/google-api-wrapper 6.1.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/lib/jsonLib.d.ts +2 -2
- package/dist/lib/jsonLib.js +11 -4
- package/dist/lib/jsonLib.js.map +1 -1
- package/dist/lib/secrets.d.ts +2 -0
- package/dist/lib/secrets.js +22 -4
- package/dist/lib/secrets.js.map +1 -1
- package/package.json +5 -2
- package/src/lib/__tests__/jsonLib.test.ts +104 -16
- package/src/lib/__tests__/secrets.test.ts +69 -11
- package/src/lib/jsonLib.ts +13 -4
- package/src/lib/secrets.ts +27 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ 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
|
+
## [7.0.0](../../tags/v7.0.0) - 2023-04-22
|
|
9
|
+
### Changed
|
|
10
|
+
- Invalidate tokens after 7 days due to Google policy for testing apps
|
|
11
|
+
|
|
12
|
+
## [6.1.1](../../tags/v6.1.1) - 2023-03-27
|
|
13
|
+
### Changed
|
|
14
|
+
- Immediately destroy server after receiving needed response
|
|
15
|
+
|
|
8
16
|
## [6.1.0](../../tags/v6.1.0) - 2023-03-24
|
|
9
17
|
### Added
|
|
10
18
|
- Overriding scopes for `getAuth` and `getAPI` functions
|
package/dist/lib/jsonLib.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ declare const _default: {
|
|
|
7
7
|
checkJSON: typeof checkJSON;
|
|
8
8
|
};
|
|
9
9
|
export default _default;
|
|
10
|
-
declare function getJSON<T>(filename: string, createCallback: () => Exclude<T, Promise<any
|
|
11
|
-
declare function getJSONAsync<T>(filename: string, createCallbackAsync: () => Promise<T>): Promise<T>;
|
|
10
|
+
declare function getJSON<T>(filename: string, createCallback: () => Exclude<T, Promise<any>>, validateJSON?: (json: T) => boolean): T;
|
|
11
|
+
declare function getJSONAsync<T>(filename: string, createCallbackAsync: () => Promise<T>, validateJSONAsync?: (json: T) => Promise<boolean>): Promise<T>;
|
|
12
12
|
declare function writeJSON<T>(filename: string, json: T): void;
|
|
13
13
|
declare function readJSON<T>(filename: string): T;
|
|
14
14
|
declare function checkJSON<T>(filename: string, json: T): void;
|
package/dist/lib/jsonLib.js
CHANGED
|
@@ -9,9 +9,12 @@ const logger_1 = require("./logger");
|
|
|
9
9
|
const paths_1 = require("./paths");
|
|
10
10
|
const jsonLib_1 = __importDefault(require("./jsonLib"));
|
|
11
11
|
exports.default = { getJSON, getJSONAsync, writeJSON, readJSON, checkJSON };
|
|
12
|
-
function getJSON(filename, createCallback) {
|
|
12
|
+
function getJSON(filename, createCallback, validateJSON) {
|
|
13
13
|
if (fs_1.default.existsSync(filename)) {
|
|
14
|
-
|
|
14
|
+
const json = jsonLib_1.default.readJSON(filename);
|
|
15
|
+
if (!validateJSON || validateJSON(json)) {
|
|
16
|
+
return json;
|
|
17
|
+
}
|
|
15
18
|
}
|
|
16
19
|
const json = createCallback();
|
|
17
20
|
jsonLib_1.default.checkJSON(filename, json);
|
|
@@ -20,12 +23,16 @@ function getJSON(filename, createCallback) {
|
|
|
20
23
|
return json;
|
|
21
24
|
}
|
|
22
25
|
exports.getJSON = getJSON;
|
|
23
|
-
async function getJSONAsync(filename, createCallbackAsync) {
|
|
26
|
+
async function getJSONAsync(filename, createCallbackAsync, validateJSONAsync) {
|
|
24
27
|
if (fs_1.default.existsSync(filename)) {
|
|
25
|
-
|
|
28
|
+
const json = jsonLib_1.default.readJSON(filename);
|
|
29
|
+
if (!validateJSONAsync || await validateJSONAsync(json)) {
|
|
30
|
+
return json;
|
|
31
|
+
}
|
|
26
32
|
}
|
|
27
33
|
const json = await createCallbackAsync();
|
|
28
34
|
jsonLib_1.default.checkJSON(filename, json);
|
|
35
|
+
(0, paths_1.ensureFile)(filename);
|
|
29
36
|
jsonLib_1.default.writeJSON(filename, json);
|
|
30
37
|
return json;
|
|
31
38
|
}
|
package/dist/lib/jsonLib.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonLib.js","sourceRoot":"","sources":["../../src/lib/jsonLib.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,qCAAiC;AACjC,mCAAqC;AAErC,wDAAgC;AAGhC,kBAAe,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AAEzE,SAAS,OAAO,CAAI,QAAgB,EAAE,cAA8C;
|
|
1
|
+
{"version":3,"file":"jsonLib.js","sourceRoot":"","sources":["../../src/lib/jsonLib.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,qCAAiC;AACjC,mCAAqC;AAErC,wDAAgC;AAGhC,kBAAe,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AAEzE,SAAS,OAAO,CAAI,QAAgB,EAAE,cAA8C,EAAE,YAAmC;IACxH,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM,IAAI,GAAG,iBAAO,CAAC,QAAQ,CAAI,QAAQ,CAAC,CAAC;QAE3C,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE;YACxC,OAAO,IAAI,CAAC;SACZ;KACD;IAED,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,IAAA,kBAAU,EAAC,QAAQ,CAAC,CAAC;IACrB,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACb,CAAC;AAjBQ,0BAAO;AAmBhB,KAAK,UAAU,YAAY,CAAI,QAAgB,EAAE,mBAAqC,EAAE,iBAAiD;IACxI,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM,IAAI,GAAG,iBAAO,CAAC,QAAQ,CAAI,QAAQ,CAAC,CAAC;QAE3C,IAAI,CAAC,iBAAiB,IAAI,MAAM,iBAAiB,CAAC,IAAI,CAAC,EAAE;YACxD,OAAO,IAAI,CAAC;SACZ;KACD;IAED,MAAM,IAAI,GAAG,MAAM,mBAAmB,EAAE,CAAC;IACzC,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,IAAA,kBAAU,EAAC,QAAQ,CAAC,CAAC;IACrB,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACb,CAAC;AAjCiB,oCAAY;AAmC9B,SAAS,SAAS,CAAI,QAAgB,EAAE,IAAO;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACtD,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAtC+B,8BAAS;AAwCzC,SAAS,QAAQ,CAAI,QAAgB;IACpC,MAAM,UAAU,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxD,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAM,CAAC;AACpC,CAAC;AAED,SAAS,SAAS,CAAI,QAAgB,EAAE,IAAO;IAC9C,IAAI,IAAI,EAAE;QACT,OAAO;KACP;IACD,IAAA,cAAK,EAAC,QAAQ,QAAQ,sGAAsG,CAAC,CAAC;AAC/H,CAAC"}
|
package/dist/lib/secrets.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ declare const _default: {
|
|
|
5
5
|
getScopes: typeof getScopes;
|
|
6
6
|
getSecrets: typeof getSecrets;
|
|
7
7
|
getCredentials: typeof getCredentials;
|
|
8
|
+
validateCredentials: typeof validateCredentials;
|
|
8
9
|
createCredentials: typeof createCredentials;
|
|
9
10
|
checkSecrets: typeof checkSecrets;
|
|
10
11
|
getSecretsError: typeof getSecretsError;
|
|
@@ -14,6 +15,7 @@ export default _default;
|
|
|
14
15
|
declare function getScopes(): string[];
|
|
15
16
|
declare function getSecrets(profile: string): Secrets;
|
|
16
17
|
declare function getCredentials(profile: string, auth: GoogleApis.Common.OAuth2Client, options?: AuthOptions): Promise<GoogleApis.Auth.Credentials>;
|
|
18
|
+
declare function validateCredentials(credentials: GoogleApis.Auth.Credentials): Promise<boolean>;
|
|
17
19
|
declare function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client, options?: AuthOptions): Promise<GoogleApis.Auth.Credentials>;
|
|
18
20
|
declare function checkSecrets(profile: string, secretsObject: Secrets, secretsFile: string): true | void;
|
|
19
21
|
declare function getScopesError(scopesFile: string): string;
|
package/dist/lib/secrets.js
CHANGED
|
@@ -28,14 +28,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.getCredentials = exports.getSecrets = void 0;
|
|
30
30
|
const http_1 = __importDefault(require("http"));
|
|
31
|
+
const server_destroy_1 = __importDefault(require("server-destroy"));
|
|
31
32
|
const colorette = __importStar(require("colorette"));
|
|
33
|
+
const open_1 = __importDefault(require("open"));
|
|
32
34
|
const jsonLib_1 = require("./jsonLib");
|
|
33
35
|
const logger_1 = require("./logger");
|
|
34
36
|
const paths_1 = require("./paths");
|
|
35
37
|
const secrets_1 = __importDefault(require("./secrets"));
|
|
36
|
-
exports.default = { getScopes, getSecrets, getCredentials, createCredentials, checkSecrets, getSecretsError, getScopesError };
|
|
38
|
+
exports.default = { getScopes, getSecrets, getCredentials, validateCredentials, createCredentials, checkSecrets, getSecretsError, getScopesError };
|
|
37
39
|
const callbackPort = 6006;
|
|
38
40
|
const callbackURI = `http://localhost:${callbackPort}/oauthcallback`;
|
|
41
|
+
const tokenExpiration = 7 * 24 * 60 * 60 * 1000;
|
|
39
42
|
function getScopes() {
|
|
40
43
|
const scopesFile = (0, paths_1.getScopesFile)();
|
|
41
44
|
const scopes = (0, jsonLib_1.getJSON)(scopesFile, () => (0, logger_1.error)(secrets_1.default.getScopesError(scopesFile)));
|
|
@@ -52,9 +55,18 @@ async function getCredentials(profile, auth, options) {
|
|
|
52
55
|
const credentialsFile = (0, paths_1.getCredentialsFile)(profile);
|
|
53
56
|
return (options === null || options === void 0 ? void 0 : options.temporary)
|
|
54
57
|
? secrets_1.default.createCredentials(profile, auth, options)
|
|
55
|
-
: (0, jsonLib_1.getJSONAsync)(credentialsFile, () => secrets_1.default.createCredentials(profile, auth, options));
|
|
58
|
+
: (0, jsonLib_1.getJSONAsync)(credentialsFile, () => secrets_1.default.createCredentials(profile, auth, options), secrets_1.default.validateCredentials);
|
|
56
59
|
}
|
|
57
60
|
exports.getCredentials = getCredentials;
|
|
61
|
+
async function validateCredentials(credentials) {
|
|
62
|
+
if (!credentials.access_token) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (!credentials.expiry_date) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return new Date().getTime() - credentials.expiry_date < tokenExpiration;
|
|
69
|
+
}
|
|
58
70
|
async function createCredentials(profile, auth, options) {
|
|
59
71
|
const scope = (options === null || options === void 0 ? void 0 : options.scopes) || secrets_1.default.getScopes();
|
|
60
72
|
return new Promise((resolve) => {
|
|
@@ -71,13 +83,19 @@ async function createCredentials(profile, auth, options) {
|
|
|
71
83
|
if (!code) {
|
|
72
84
|
return;
|
|
73
85
|
}
|
|
74
|
-
server.
|
|
86
|
+
server.destroy();
|
|
75
87
|
const { tokens } = await auth.getToken(code);
|
|
76
88
|
resolve(tokens);
|
|
77
89
|
}
|
|
78
90
|
});
|
|
91
|
+
(0, server_destroy_1.default)(server);
|
|
79
92
|
server.listen(callbackPort);
|
|
80
|
-
|
|
93
|
+
if (options === null || options === void 0 ? void 0 : options.temporary) {
|
|
94
|
+
(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(','))}`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
(0, open_1.default)(authUrl);
|
|
98
|
+
}
|
|
81
99
|
});
|
|
82
100
|
}
|
|
83
101
|
function checkSecrets(profile, secretsObject, secretsFile) {
|
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;
|
|
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": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Provides quick interface for getting google API data",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"google",
|
|
@@ -31,10 +31,13 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"colorette": "^2.0.19",
|
|
33
33
|
"execa": "^5.1.1",
|
|
34
|
-
"googleapis": "^104.0.0"
|
|
34
|
+
"googleapis": "^104.0.0",
|
|
35
|
+
"open": "^9.1.0",
|
|
36
|
+
"server-destroy": "^1.0.1"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@types/jest": "^28.1.3",
|
|
40
|
+
"@types/server-destroy": "^1.0.1",
|
|
38
41
|
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
|
39
42
|
"@typescript-eslint/parser": "^5.30.0",
|
|
40
43
|
"eslint": "^8.18.0",
|
|
@@ -33,11 +33,15 @@ const json = { key : 'value' };
|
|
|
33
33
|
const jsonString = JSON.stringify(json, null, ' ');
|
|
34
34
|
const fallbackJSON = { fallbackKey : 'fallbackValue' };
|
|
35
35
|
|
|
36
|
+
let fileExists: boolean;
|
|
37
|
+
let validation: boolean;
|
|
38
|
+
|
|
39
|
+
const validateCallback = jest.fn().mockImplementation(() => validation);
|
|
40
|
+
const validateCallbackAsync = jest.fn().mockImplementation(async () => validation);
|
|
41
|
+
|
|
36
42
|
const createCallback = jest.fn().mockReturnValue(fallbackJSON);
|
|
37
43
|
const createCallbackAsync = jest.fn().mockResolvedValue(fallbackJSON);
|
|
38
44
|
|
|
39
|
-
let fileExists: boolean;
|
|
40
|
-
|
|
41
45
|
describe('src/lib/jsonLib', () => {
|
|
42
46
|
describe('readJSON', () => {
|
|
43
47
|
it('should read specified file', () => {
|
|
@@ -62,91 +66,175 @@ describe('src/lib/jsonLib', () => {
|
|
|
62
66
|
});
|
|
63
67
|
|
|
64
68
|
describe('getJSON', () => {
|
|
65
|
-
|
|
69
|
+
|
|
70
|
+
it('should call readJSON if file exists and json is valid', () => {
|
|
66
71
|
fileExists = true;
|
|
72
|
+
validation = true;
|
|
67
73
|
|
|
68
|
-
original.getJSON(filename, createCallback);
|
|
74
|
+
original.getJSON(filename, createCallback, validateCallback);
|
|
69
75
|
|
|
70
76
|
expect(jsonLib.readJSON).toBeCalledWith(filename);
|
|
71
77
|
expect(createCallback).not.toBeCalled();
|
|
72
78
|
});
|
|
73
79
|
|
|
80
|
+
it('should call createCallback if file exists but json is not valid', () => {
|
|
81
|
+
fileExists = true;
|
|
82
|
+
validation = false;
|
|
83
|
+
|
|
84
|
+
original.getJSON(filename, createCallback, validateCallback);
|
|
85
|
+
|
|
86
|
+
expect(jsonLib.readJSON).toBeCalledWith(filename);
|
|
87
|
+
expect(createCallback).toBeCalledWith();
|
|
88
|
+
});
|
|
89
|
+
|
|
74
90
|
it('should call createCallback if file not exists', () => {
|
|
75
91
|
fileExists = false;
|
|
76
92
|
|
|
77
|
-
original.getJSON(filename, createCallback);
|
|
93
|
+
original.getJSON(filename, createCallback, validateCallback);
|
|
78
94
|
|
|
79
95
|
expect(jsonLib.readJSON).not.toBeCalled();
|
|
80
96
|
expect(createCallback).toBeCalledWith();
|
|
81
97
|
});
|
|
82
98
|
|
|
99
|
+
it('should not write fallback JSON back if file exists and json is valid', () => {
|
|
100
|
+
fileExists = true;
|
|
101
|
+
validation = true;
|
|
102
|
+
|
|
103
|
+
original.getJSON(filename, createCallback, validateCallback);
|
|
104
|
+
|
|
105
|
+
expect(jsonLib.writeJSON).not.toBeCalled();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should write fallback JSON back if file exists but json is not valid', () => {
|
|
109
|
+
fileExists = true;
|
|
110
|
+
validation = false;
|
|
111
|
+
|
|
112
|
+
original.getJSON(filename, createCallback, validateCallback);
|
|
113
|
+
|
|
114
|
+
expect(jsonLib.checkJSON).toBeCalledWith(filename, fallbackJSON);
|
|
115
|
+
expect(paths.ensureFile).toBeCalledWith(filename);
|
|
116
|
+
expect(jsonLib.writeJSON).toBeCalledWith(filename, fallbackJSON);
|
|
117
|
+
});
|
|
118
|
+
|
|
83
119
|
it('should write fallback JSON back if file not exists', () => {
|
|
84
120
|
fileExists = false;
|
|
85
121
|
|
|
86
|
-
original.getJSON(filename, createCallback);
|
|
122
|
+
original.getJSON(filename, createCallback, validateCallback);
|
|
87
123
|
|
|
88
124
|
expect(jsonLib.checkJSON).toBeCalledWith(filename, fallbackJSON);
|
|
89
125
|
expect(paths.ensureFile).toBeCalledWith(filename);
|
|
90
126
|
expect(jsonLib.writeJSON).toBeCalledWith(filename, fallbackJSON);
|
|
91
127
|
});
|
|
92
128
|
|
|
93
|
-
it('should return JSON if file exists', () => {
|
|
129
|
+
it('should return JSON if file exists and json is valid', () => {
|
|
94
130
|
fileExists = true;
|
|
131
|
+
validation = true;
|
|
95
132
|
|
|
96
|
-
const result = original.getJSON(filename, createCallback);
|
|
133
|
+
const result = original.getJSON(filename, createCallback, validateCallback);
|
|
97
134
|
|
|
98
135
|
expect(result).toEqual(json);
|
|
99
136
|
});
|
|
100
137
|
|
|
138
|
+
it('should return fallback JSON if file exists but json is not valid', () => {
|
|
139
|
+
fileExists = true;
|
|
140
|
+
validation = false;
|
|
141
|
+
|
|
142
|
+
const result = original.getJSON(filename, createCallback, validateCallback);
|
|
143
|
+
|
|
144
|
+
expect(result).toEqual(fallbackJSON);
|
|
145
|
+
});
|
|
146
|
+
|
|
101
147
|
it('should return fallback JSON if file not exists', () => {
|
|
102
148
|
fileExists = false;
|
|
103
149
|
|
|
104
|
-
const result = original.getJSON(filename, createCallback);
|
|
150
|
+
const result = original.getJSON(filename, createCallback, validateCallback);
|
|
105
151
|
|
|
106
152
|
expect(result).toEqual(fallbackJSON);
|
|
107
153
|
});
|
|
108
154
|
});
|
|
109
155
|
|
|
110
156
|
describe('getJSONAsync', () => {
|
|
111
|
-
it('should call readJSON if file exists', async () => {
|
|
157
|
+
it('should call readJSON if file exists and json is valid', async () => {
|
|
112
158
|
fileExists = true;
|
|
159
|
+
validation = true;
|
|
113
160
|
|
|
114
|
-
await original.getJSONAsync(filename, createCallbackAsync);
|
|
161
|
+
await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
115
162
|
|
|
116
163
|
expect(jsonLib.readJSON).toBeCalledWith(filename);
|
|
117
164
|
expect(createCallbackAsync).not.toBeCalled();
|
|
118
165
|
});
|
|
119
166
|
|
|
167
|
+
it('should call createCallback if file exists but json is not valid', async () => {
|
|
168
|
+
fileExists = true;
|
|
169
|
+
validation = false;
|
|
170
|
+
|
|
171
|
+
await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
172
|
+
|
|
173
|
+
expect(jsonLib.readJSON).toBeCalledWith(filename);
|
|
174
|
+
expect(createCallbackAsync).toBeCalledWith();
|
|
175
|
+
});
|
|
176
|
+
|
|
120
177
|
it('should call createCallback if file not exists', async () => {
|
|
121
178
|
fileExists = false;
|
|
122
179
|
|
|
123
|
-
await original.getJSONAsync(filename, createCallbackAsync);
|
|
180
|
+
await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
124
181
|
|
|
125
182
|
expect(jsonLib.readJSON).not.toBeCalled();
|
|
126
183
|
expect(createCallbackAsync).toBeCalledWith();
|
|
127
184
|
});
|
|
128
185
|
|
|
186
|
+
it('should not write fallback JSON back if file exists and json is valid', async () => {
|
|
187
|
+
fileExists = true;
|
|
188
|
+
validation = true;
|
|
189
|
+
|
|
190
|
+
await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
191
|
+
|
|
192
|
+
expect(jsonLib.writeJSON).not.toBeCalled();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should write fallback JSON back if file exists but json is not valid', async () => {
|
|
196
|
+
fileExists = true;
|
|
197
|
+
validation = false;
|
|
198
|
+
|
|
199
|
+
await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
200
|
+
|
|
201
|
+
expect(jsonLib.checkJSON).toBeCalledWith(filename, fallbackJSON);
|
|
202
|
+
expect(paths.ensureFile).toBeCalledWith(filename);
|
|
203
|
+
expect(jsonLib.writeJSON).toBeCalledWith(filename, fallbackJSON);
|
|
204
|
+
});
|
|
205
|
+
|
|
129
206
|
it('should write fallback JSON back if file not exists', async () => {
|
|
130
207
|
fileExists = false;
|
|
131
208
|
|
|
132
|
-
await original.getJSONAsync(filename, createCallbackAsync);
|
|
209
|
+
await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
133
210
|
|
|
134
211
|
expect(jsonLib.checkJSON).toBeCalledWith(filename, fallbackJSON);
|
|
212
|
+
expect(paths.ensureFile).toBeCalledWith(filename);
|
|
135
213
|
expect(jsonLib.writeJSON).toBeCalledWith(filename, fallbackJSON);
|
|
136
214
|
});
|
|
137
215
|
|
|
138
|
-
it('should return JSON if file exists', async () => {
|
|
216
|
+
it('should return JSON if file exists and json is valid', async () => {
|
|
139
217
|
fileExists = true;
|
|
218
|
+
validation = true;
|
|
140
219
|
|
|
141
|
-
const result = await original.getJSONAsync(filename, createCallbackAsync);
|
|
220
|
+
const result = await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
142
221
|
|
|
143
222
|
expect(result).toEqual(json);
|
|
144
223
|
});
|
|
145
224
|
|
|
225
|
+
it('should return fallback JSON if file exists but json is not valid', async () => {
|
|
226
|
+
fileExists = true;
|
|
227
|
+
validation = false;
|
|
228
|
+
|
|
229
|
+
const result = await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
230
|
+
|
|
231
|
+
expect(result).toEqual(fallbackJSON);
|
|
232
|
+
});
|
|
233
|
+
|
|
146
234
|
it('should return fallback JSON if file not exists', async () => {
|
|
147
235
|
fileExists = false;
|
|
148
236
|
|
|
149
|
-
const result = await original.getJSONAsync(filename, createCallbackAsync);
|
|
237
|
+
const result = await original.getJSONAsync(filename, createCallbackAsync, validateCallbackAsync);
|
|
150
238
|
|
|
151
239
|
expect(result).toEqual(fallbackJSON);
|
|
152
240
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import * as colorette from 'colorette';
|
|
4
|
+
import open from 'open';
|
|
4
5
|
import type GoogleApis from 'googleapis';
|
|
5
6
|
import jsonLib from '../jsonLib';
|
|
6
7
|
import logger from '../logger';
|
|
@@ -9,13 +10,14 @@ import type { Secrets } from '../../types';
|
|
|
9
10
|
import secrets from '../secrets';
|
|
10
11
|
const original = jest.requireActual('../secrets').default as typeof secrets;
|
|
11
12
|
jest.mock<typeof secrets>('../secrets', () => ({
|
|
12
|
-
getScopes
|
|
13
|
-
getSecrets
|
|
14
|
-
getCredentials
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
getScopes : jest.fn().mockImplementation(() => scopesJSON),
|
|
14
|
+
getSecrets : jest.fn().mockImplementation(() => secretsJSON),
|
|
15
|
+
getCredentials : jest.fn(),
|
|
16
|
+
validateCredentials : jest.fn(),
|
|
17
|
+
createCredentials : jest.fn(),
|
|
18
|
+
checkSecrets : jest.fn(),
|
|
19
|
+
getScopesError : jest.fn().mockImplementation(() => scopesError),
|
|
20
|
+
getSecretsError : jest.fn().mockImplementation(() => secretsError),
|
|
19
21
|
}));
|
|
20
22
|
|
|
21
23
|
jest.mock<Partial<typeof http>>('http', () => ({
|
|
@@ -23,8 +25,10 @@ jest.mock<Partial<typeof http>>('http', () => ({
|
|
|
23
25
|
serverCallback = callback;
|
|
24
26
|
|
|
25
27
|
return {
|
|
28
|
+
on,
|
|
26
29
|
listen,
|
|
27
30
|
close,
|
|
31
|
+
destroy,
|
|
28
32
|
};
|
|
29
33
|
}),
|
|
30
34
|
}));
|
|
@@ -37,6 +41,8 @@ jest.mock<Partial<typeof colorette>>('colorette', () => ({
|
|
|
37
41
|
yellow : jest.fn().mockImplementation((text) => `yellow:${text}`),
|
|
38
42
|
}));
|
|
39
43
|
|
|
44
|
+
jest.mock<Partial<typeof open>>('open', () => jest.fn());
|
|
45
|
+
|
|
40
46
|
jest.mock<Partial<typeof jsonLib>>('../jsonLib', () => ({
|
|
41
47
|
getJSON : jest.fn().mockImplementation(() => json),
|
|
42
48
|
getJSONAsync : jest.fn().mockImplementation(async () => json),
|
|
@@ -87,7 +93,7 @@ const code = 'code';
|
|
|
87
93
|
const authUrl = 'https://authUrl';
|
|
88
94
|
const auth = {
|
|
89
95
|
generateAuthUrl : jest.fn().mockReturnValue(authUrl),
|
|
90
|
-
getToken
|
|
96
|
+
getToken : jest.fn().mockResolvedValue({ tokens : credentialsJSON }),
|
|
91
97
|
} as unknown as GoogleApis.Common.OAuth2Client;
|
|
92
98
|
|
|
93
99
|
let request: http.IncomingMessage;
|
|
@@ -103,10 +109,24 @@ let serverCallback: (
|
|
|
103
109
|
|
|
104
110
|
let closedTime: number;
|
|
105
111
|
|
|
112
|
+
const on = jest.fn().mockImplementation((event: string, listener: (...args: any[]) => void) => {
|
|
113
|
+
if (event === 'connection') {
|
|
114
|
+
// always simulate opening several connections once connections are meant to be listened
|
|
115
|
+
connections.forEach((connection) => listener(connection));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
106
119
|
const listen = jest.fn();
|
|
107
120
|
const close = jest.fn().mockImplementation(() => {
|
|
108
121
|
closedTime = new Date().getTime();
|
|
109
122
|
});
|
|
123
|
+
const destroy = jest.fn();
|
|
124
|
+
|
|
125
|
+
const connections = [
|
|
126
|
+
{ remoteAddress : 'server', remotePort : '1001', on : jest.fn(), destroy : jest.fn() },
|
|
127
|
+
{ remoteAddress : 'server', remotePort : '1002', on : jest.fn(), destroy : jest.fn() },
|
|
128
|
+
{ remoteAddress : 'server', remotePort : '1003', on : jest.fn(), destroy : jest.fn() },
|
|
129
|
+
];
|
|
110
130
|
|
|
111
131
|
describe('src/lib/secrets', () => {
|
|
112
132
|
describe('getScopes', () => {
|
|
@@ -239,6 +259,31 @@ describe('src/lib/secrets', () => {
|
|
|
239
259
|
});
|
|
240
260
|
});
|
|
241
261
|
|
|
262
|
+
describe('validateCredentials', () => {
|
|
263
|
+
it('should return false if no access token', async () => {
|
|
264
|
+
expect(await original.validateCredentials({})).toEqual(false);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should return true if no expiration', async () => {
|
|
268
|
+
// eslint-disable-next-line camelcase
|
|
269
|
+
expect(await original.validateCredentials({ access_token : 'token' })).toEqual(true);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should return true if credentials are not more than 1 week ago', async () => {
|
|
273
|
+
const expiryDate = new Date();
|
|
274
|
+
expiryDate.setDate(expiryDate.getDate() - 6);
|
|
275
|
+
// eslint-disable-next-line camelcase
|
|
276
|
+
expect(await original.validateCredentials({ access_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(true);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should return true if credentials are more than 1 week ago', async () => {
|
|
280
|
+
const expiryDate = new Date();
|
|
281
|
+
expiryDate.setDate(expiryDate.getDate() - 8);
|
|
282
|
+
// eslint-disable-next-line camelcase
|
|
283
|
+
expect(await original.validateCredentials({ access_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(false);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
242
287
|
describe('createCredentials', () => {
|
|
243
288
|
function willOpen(request: http.IncomingMessage, timeout: number) {
|
|
244
289
|
setTimeout(async () => {
|
|
@@ -291,19 +336,30 @@ describe('src/lib/secrets', () => {
|
|
|
291
336
|
expect(listen).toBeCalledWith(6006);
|
|
292
337
|
});
|
|
293
338
|
|
|
294
|
-
it('should
|
|
339
|
+
it('should open browser page without prompt if this is permanent request that tends to save credentials in the file', async () => {
|
|
295
340
|
willOpen(request, 100);
|
|
296
341
|
|
|
297
342
|
await original.createCredentials(profile, auth);
|
|
298
343
|
|
|
344
|
+
expect(open).toBeCalledWith('https://authUrl');
|
|
345
|
+
expect(logger.info).not.toBeCalled();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should ask to open browser page', async () => {
|
|
349
|
+
willOpen(request, 100);
|
|
350
|
+
|
|
351
|
+
await original.createCredentials(profile, auth, { temporary : true });
|
|
352
|
+
|
|
353
|
+
expect(open).not.toBeCalled();
|
|
299
354
|
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`);
|
|
300
355
|
});
|
|
301
356
|
|
|
302
357
|
it('should ask to open browser page with custom scopes', async () => {
|
|
303
358
|
willOpen(request, 100);
|
|
304
359
|
|
|
305
|
-
await original.createCredentials(profile, auth, { scopes : [ 'scope1', 'scope2' ] });
|
|
360
|
+
await original.createCredentials(profile, auth, { temporary : true, scopes : [ 'scope1', 'scope2' ] });
|
|
306
361
|
|
|
362
|
+
expect(open).not.toBeCalled();
|
|
307
363
|
expect(logger.info).toBeCalledWith(`Please open yellow:https://authUrl in your browser using google profile for yellow:${profile} and allow access to yellow:scope1,scope2`);
|
|
308
364
|
});
|
|
309
365
|
|
|
@@ -315,12 +371,14 @@ describe('src/lib/secrets', () => {
|
|
|
315
371
|
expect(response.end).toBeCalledWith('<h1>Please close this page and return to application. Wait for application to be finished automatically.</h1>');
|
|
316
372
|
});
|
|
317
373
|
|
|
318
|
-
it('should close server if request.url is truthy', async () => {
|
|
374
|
+
it('should close server and destroy all connections if request.url is truthy', async () => {
|
|
319
375
|
willOpen(request, 100);
|
|
320
376
|
|
|
321
377
|
await original.createCredentials(profile, auth);
|
|
322
378
|
|
|
323
379
|
expect(close).toBeCalled();
|
|
380
|
+
|
|
381
|
+
connections.forEach((connection) => expect(connection.destroy).toBeCalled());
|
|
324
382
|
});
|
|
325
383
|
|
|
326
384
|
it('should only resolve when request.url is truthy', async () => {
|
package/src/lib/jsonLib.ts
CHANGED
|
@@ -7,9 +7,13 @@ import jsonLib from './jsonLib';
|
|
|
7
7
|
export { getJSON, getJSONAsync, writeJSON };
|
|
8
8
|
export default { getJSON, getJSONAsync, writeJSON, readJSON, checkJSON };
|
|
9
9
|
|
|
10
|
-
function getJSON<T>(filename: string, createCallback: () => Exclude<T, Promise<any
|
|
10
|
+
function getJSON<T>(filename: string, createCallback: () => Exclude<T, Promise<any>>, validateJSON?: (json: T) => boolean): T {
|
|
11
11
|
if (fs.existsSync(filename)) {
|
|
12
|
-
|
|
12
|
+
const json = jsonLib.readJSON<T>(filename);
|
|
13
|
+
|
|
14
|
+
if (!validateJSON || validateJSON(json)) {
|
|
15
|
+
return json;
|
|
16
|
+
}
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
const json = createCallback();
|
|
@@ -19,13 +23,18 @@ function getJSON<T>(filename: string, createCallback: () => Exclude<T, Promise<a
|
|
|
19
23
|
return json;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
async function getJSONAsync<T>(filename: string, createCallbackAsync: () => Promise<T>): Promise<T> {
|
|
26
|
+
async function getJSONAsync<T>(filename: string, createCallbackAsync: () => Promise<T>, validateJSONAsync?: (json: T) => Promise<boolean>): Promise<T> {
|
|
23
27
|
if (fs.existsSync(filename)) {
|
|
24
|
-
|
|
28
|
+
const json = jsonLib.readJSON<T>(filename);
|
|
29
|
+
|
|
30
|
+
if (!validateJSONAsync || await validateJSONAsync(json)) {
|
|
31
|
+
return json;
|
|
32
|
+
}
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
const json = await createCallbackAsync();
|
|
28
36
|
jsonLib.checkJSON(filename, json);
|
|
37
|
+
ensureFile(filename);
|
|
29
38
|
jsonLib.writeJSON(filename, json);
|
|
30
39
|
return json;
|
|
31
40
|
}
|
package/src/lib/secrets.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
|
+
import enableDestroy from 'server-destroy';
|
|
2
3
|
import * as colorette from 'colorette';
|
|
4
|
+
import open from 'open';
|
|
3
5
|
import type GoogleApis from 'googleapis';
|
|
4
6
|
import type { Secrets, AuthOptions } from '../types';
|
|
5
7
|
import { getJSON, getJSONAsync } from './jsonLib';
|
|
@@ -9,10 +11,11 @@ import { getScopesFile, getSecretsFile, getCredentialsFile } from './paths';
|
|
|
9
11
|
import secrets from './secrets';
|
|
10
12
|
|
|
11
13
|
export { getSecrets, getCredentials };
|
|
12
|
-
export default { getScopes, getSecrets, getCredentials, createCredentials, checkSecrets, getSecretsError, getScopesError };
|
|
14
|
+
export default { getScopes, getSecrets, getCredentials, validateCredentials, createCredentials, checkSecrets, getSecretsError, getScopesError };
|
|
13
15
|
|
|
14
|
-
const callbackPort
|
|
15
|
-
const callbackURI
|
|
16
|
+
const callbackPort = 6006;
|
|
17
|
+
const callbackURI = `http://localhost:${callbackPort}/oauthcallback`;
|
|
18
|
+
const tokenExpiration = 7 * 24 * 60 * 60 * 1000;
|
|
16
19
|
|
|
17
20
|
function getScopes(): string[] {
|
|
18
21
|
const scopesFile = getScopesFile();
|
|
@@ -32,7 +35,19 @@ async function getCredentials(profile: string, auth: GoogleApis.Common.OAuth2Cli
|
|
|
32
35
|
|
|
33
36
|
return options?.temporary
|
|
34
37
|
? secrets.createCredentials(profile, auth, options)
|
|
35
|
-
: getJSONAsync(credentialsFile, () => secrets.createCredentials(profile, auth, options));
|
|
38
|
+
: getJSONAsync(credentialsFile, () => secrets.createCredentials(profile, auth, options), secrets.validateCredentials);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function validateCredentials(credentials: GoogleApis.Auth.Credentials): Promise<boolean> {
|
|
42
|
+
if (!credentials.access_token) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!credentials.expiry_date) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return new Date().getTime() - credentials.expiry_date < tokenExpiration;
|
|
36
51
|
}
|
|
37
52
|
|
|
38
53
|
async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client, options?: AuthOptions): Promise<GoogleApis.Auth.Credentials> {
|
|
@@ -56,14 +71,20 @@ async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Cl
|
|
|
56
71
|
return;
|
|
57
72
|
}
|
|
58
73
|
|
|
59
|
-
server.
|
|
74
|
+
server.destroy();
|
|
60
75
|
const { tokens } = await auth.getToken(code);
|
|
61
76
|
resolve(tokens);
|
|
62
77
|
}
|
|
63
78
|
});
|
|
64
79
|
|
|
80
|
+
enableDestroy(server);
|
|
65
81
|
server.listen(callbackPort);
|
|
66
|
-
|
|
82
|
+
|
|
83
|
+
if (options?.temporary) {
|
|
84
|
+
info(`Please open ${colorette.yellow(authUrl)} in your browser using google profile for ${colorette.yellow(profile)} and allow access to ${colorette.yellow(scope.join(','))}`);
|
|
85
|
+
} else {
|
|
86
|
+
open(authUrl);
|
|
87
|
+
}
|
|
67
88
|
});
|
|
68
89
|
}
|
|
69
90
|
|