@anmiles/google-api-wrapper 14.1.0 → 14.2.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 CHANGED
@@ -5,6 +5,10 @@ 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
+ ## [14.2.0](../../tags/v14.2.0) - 2023-09-11
9
+ ### Changed
10
+ - Beautify authorization page
11
+
8
12
  ## [14.1.0](../../tags/v14.1.0) - 2023-08-06
9
13
  ### Added
10
14
  - Filtering profiles
@@ -1,12 +1,15 @@
1
- export { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile };
1
+ import type { templates } from './renderer';
2
+ export { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile, getTemplateFile };
2
3
  declare const _default: {
3
4
  getProfilesFile: typeof getProfilesFile;
4
5
  getScopesFile: typeof getScopesFile;
5
6
  getSecretsFile: typeof getSecretsFile;
6
7
  getCredentialsFile: typeof getCredentialsFile;
8
+ getTemplateFile: typeof getTemplateFile;
7
9
  };
8
10
  export default _default;
9
11
  declare function getProfilesFile(): string;
10
12
  declare function getScopesFile(): string;
11
13
  declare function getSecretsFile(profile: string): string;
12
14
  declare function getCredentialsFile(profile: string): string;
15
+ declare function getTemplateFile(templateName: keyof typeof templates): string;
package/dist/lib/paths.js CHANGED
@@ -3,12 +3,14 @@ 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.getScopesFile = exports.getProfilesFile = void 0;
6
+ exports.getTemplateFile = exports.getCredentialsFile = exports.getSecretsFile = exports.getScopesFile = exports.getProfilesFile = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
- exports.default = { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile };
8
+ exports.default = { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile, getTemplateFile };
9
9
  const dirPaths = {
10
10
  input: 'input',
11
11
  secrets: 'secrets',
12
+ // TODO: Remove this hack after moving to React
13
+ templates: 'node_modules/@anmiles/google-api-wrapper/dist/templates',
12
14
  };
13
15
  function getProfilesFile() {
14
16
  return path_1.default.join(dirPaths.input, 'profiles.json');
@@ -26,4 +28,8 @@ function getCredentialsFile(profile) {
26
28
  return path_1.default.join(dirPaths.secrets, `${profile}.credentials.json`);
27
29
  }
28
30
  exports.getCredentialsFile = getCredentialsFile;
31
+ function getTemplateFile(templateName) {
32
+ return path_1.default.join(dirPaths.templates, `${templateName}.html`);
33
+ }
34
+ exports.getTemplateFile = getTemplateFile;
29
35
  //# sourceMappingURL=paths.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAGxB,kBAAe,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;AAEtF,MAAM,QAAQ,GAAG;IAChB,KAAK,EAAK,OAAO;IACjB,OAAO,EAAG,SAAS;CACnB,CAAC;AAEF,SAAS,eAAe;IACvB,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;AACnD,CAAC;AAVQ,0CAAe;AAYxB,SAAS,aAAa;IACrB,OAAO,aAAa,CAAC;AACtB,CAAC;AAdyB,sCAAa;AAgBvC,SAAS,cAAc,CAAC,OAAe;IACtC,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;AACvD,CAAC;AAlBwC,wCAAc;AAoBvD,SAAS,kBAAkB,CAAC,OAAe;IAC1C,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,mBAAmB,CAAC,CAAC;AACnE,CAAC;AAtBwD,gDAAkB"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAIxB,kBAAe,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,CAAC;AAEvG,MAAM,QAAQ,GAAG;IAChB,KAAK,EAAO,OAAO;IACnB,OAAO,EAAK,SAAS;IACrB,+CAA+C;IAC/C,SAAS,EAAG,yDAAyD;CACrE,CAAC;AAEF,SAAS,eAAe;IACvB,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;AACnD,CAAC;AAZQ,0CAAe;AAcxB,SAAS,aAAa;IACrB,OAAO,aAAa,CAAC;AACtB,CAAC;AAhByB,sCAAa;AAkBvC,SAAS,cAAc,CAAC,OAAe;IACtC,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;AACvD,CAAC;AApBwC,wCAAc;AAsBvD,SAAS,kBAAkB,CAAC,OAAe;IAC1C,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,mBAAmB,CAAC,CAAC;AACnE,CAAC;AAxBwD,gDAAkB;AA0B3E,SAAS,eAAe,CAAC,YAAoC;IAC5D,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,GAAG,YAAY,OAAO,CAAC,CAAC;AAC9D,CAAC;AA5B4E,0CAAe"}
@@ -0,0 +1,31 @@
1
+ export { templates, renderAuth, renderDone };
2
+ declare const templates: {
3
+ readonly page: readonly ["content"];
4
+ readonly auth: readonly ["profile", "authUrl", "scopesList"];
5
+ readonly scope: readonly ["type", "name"];
6
+ readonly done: readonly ["profile"];
7
+ };
8
+ type TemplateName = keyof typeof templates;
9
+ declare function renderAuth({ profile, authUrl, scope }: {
10
+ profile: string;
11
+ authUrl: string;
12
+ scope: string[];
13
+ }): string;
14
+ declare function renderDone({ profile }: {
15
+ profile: string;
16
+ }): string;
17
+ declare function render<T extends TemplateName>(templateName: T, values: Record<typeof templates[T][number], string>): string;
18
+ declare function getTemplate(templateName: TemplateName): string;
19
+ declare const _default: {
20
+ templates: {
21
+ readonly page: readonly ["content"];
22
+ readonly auth: readonly ["profile", "authUrl", "scopesList"];
23
+ readonly scope: readonly ["type", "name"];
24
+ readonly done: readonly ["profile"];
25
+ };
26
+ render: typeof render;
27
+ getTemplate: typeof getTemplate;
28
+ renderAuth: typeof renderAuth;
29
+ renderDone: typeof renderDone;
30
+ };
31
+ export default _default;
@@ -0,0 +1,51 @@
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.renderDone = exports.renderAuth = exports.templates = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const renderer_1 = __importDefault(require("./renderer"));
9
+ const paths_1 = require("./paths");
10
+ const templates = {
11
+ page: ['content'],
12
+ auth: ['profile', 'authUrl', 'scopesList'],
13
+ scope: ['type', 'name'],
14
+ done: ['profile'],
15
+ };
16
+ exports.templates = templates;
17
+ const allHTML = {};
18
+ function renderAuth({ profile, authUrl, scope }) {
19
+ const scopesList = scope.map((s) => render('scope', {
20
+ name: s.split('/').pop(),
21
+ type: s.endsWith('.readonly') ? 'readonly' : '',
22
+ })).join('\n');
23
+ const content = render('auth', { profile, authUrl, scopesList });
24
+ return render('page', { content });
25
+ }
26
+ exports.renderAuth = renderAuth;
27
+ function renderDone({ profile }) {
28
+ const content = render('done', { profile });
29
+ return render('page', { content });
30
+ }
31
+ exports.renderDone = renderDone;
32
+ // TODO: Use react
33
+ function render(templateName, values) {
34
+ let html = renderer_1.default.getTemplate(templateName);
35
+ const allValues = values;
36
+ for (const variable of templates[templateName]) {
37
+ const value = allValues[variable] || '';
38
+ html = html.replace(`\${${variable}}`, value);
39
+ }
40
+ return html;
41
+ }
42
+ function getTemplate(templateName) {
43
+ if (!(templateName in allHTML)) {
44
+ const file = (0, paths_1.getTemplateFile)(templateName);
45
+ const template = fs_1.default.readFileSync(file).toString();
46
+ allHTML[templateName] = template;
47
+ }
48
+ return allHTML[templateName];
49
+ }
50
+ exports.default = { templates, render, getTemplate, renderAuth, renderDone };
51
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../src/lib/renderer.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,0DAAkC;AAClC,mCAA0C;AAI1C,MAAM,SAAS,GAAG;IACjB,IAAI,EAAI,CAAE,SAAS,CAAW;IAC9B,IAAI,EAAI,CAAE,SAAS,EAAE,SAAS,EAAE,YAAY,CAAE;IAC9C,KAAK,EAAG,CAAE,MAAM,EAAE,MAAM,CAAW;IACnC,IAAI,EAAI,CAAE,SAAS,CAAW;CACrB,CAAC;AAPF,8BAAS;AAWlB,MAAM,OAAO,GAAG,EAAkC,CAAC;AAEnD,SAAS,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAyD;IACrG,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE;QACnD,IAAI,EAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAY;QACnC,IAAI,EAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;KAChD,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEf,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;IACjE,OAAO,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AACpC,CAAC;AArBmB,gCAAU;AAuB9B,SAAS,UAAU,CAAC,EAAE,OAAO,EAAuB;IACnD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AACpC,CAAC;AA1B+B,gCAAU;AA4B1C,kBAAkB;AAClB,SAAS,MAAM,CAAyB,YAAe,EAAE,MAAmD;IAC3G,IAAI,IAAI,GAAU,kBAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,MAAgE,CAAC;IAEnF,KAAK,MAAM,QAAQ,IAAI,SAAS,CAAC,YAAY,CAAC,EAAE;QAC/C,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,GAAU,IAAI,CAAC,OAAO,CAAC,MAAM,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;KACrD;IAED,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,YAA0B;IAC9C,IAAI,CAAC,CAAC,YAAY,IAAI,OAAO,CAAC,EAAE;QAC/B,MAAM,IAAI,GAAc,IAAA,uBAAe,EAAC,YAAY,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAU,YAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QACzD,OAAO,CAAC,YAAY,CAAC,GAAG,QAAQ,CAAC;KACjC;IAED,OAAO,OAAO,CAAC,YAAY,CAAC,CAAC;AAC9B,CAAC;AAED,kBAAe,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC"}
@@ -11,6 +11,7 @@ const open_1 = __importDefault(require("open"));
11
11
  const logger_1 = require("@anmiles/logger");
12
12
  require("@anmiles/prototypes");
13
13
  const paths_1 = require("./paths");
14
+ const renderer_1 = require("./renderer");
14
15
  const secrets_1 = __importDefault(require("./secrets"));
15
16
  exports.default = { getScopes, getSecrets, getCredentials, validateCredentials, createCredentials, deleteCredentials, checkSecrets, getSecretsError, getScopesError };
16
17
  const port = 6006;
@@ -82,11 +83,10 @@ async function createCredentials(profile, auth, options, prompt) {
82
83
  const url = new URL(`http://${request.headers.host}${request.url}`);
83
84
  const code = url.searchParams.get('code');
84
85
  if (!code) {
85
- const scopesList = scope.map((s) => `<li>${s}</li>`).join('');
86
- response.end(formatMessage(`<p>Please open <a href="${authUrl}">auth page</a> using <strong>${profile}</strong> google profile</p>\n<p>Required scopes:</p>\n<ul>${scopesList}</ul>`));
86
+ response.end((0, renderer_1.renderAuth)({ profile, authUrl, scope }));
87
87
  return;
88
88
  }
89
- response.end(formatMessage('<p>Please close this page and return to application</p>'));
89
+ response.end((0, renderer_1.renderDone)({ profile }));
90
90
  server.destroy();
91
91
  const { tokens } = await auth.getToken(code);
92
92
  resolve(tokens);
@@ -113,15 +113,6 @@ function deleteCredentials(profile) {
113
113
  }
114
114
  }
115
115
  exports.deleteCredentials = deleteCredentials;
116
- function formatMessage(message) {
117
- return [
118
- '<div style="width: 100%;height: 100%;display: flex;align-items: start;justify-content: center">',
119
- '<div style="padding: 0 1em;border: 1px solid black;font-family: Arial, sans-serif;margin: 1em;">',
120
- message,
121
- '</div>',
122
- '</div>',
123
- ].join('\n');
124
- }
125
116
  function checkSecrets(profile, secretsObject, secretsFile) {
126
117
  if (secretsObject.web.redirect_uris[0] === callbackURI) {
127
118
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ <h1>Welcome ${profile}!</h1>
2
+ <p>Please authorize:</p>
3
+ <ul>
4
+ ${scopesList}
5
+ </ul>
6
+ <a href="${authUrl}">Continue</a>
@@ -0,0 +1,3 @@
1
+ <h1>Welcome ${profile}!</h1>
2
+ <p>Authorization succeeded.</p>
3
+ <p>Please close this page and return to application.</p>
@@ -0,0 +1,109 @@
1
+ <style type="text/css">
2
+ * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ html, body {
7
+ width: 100%;
8
+ height: 100%;
9
+ margin: 0;
10
+ padding: 0;
11
+ }
12
+
13
+ body {
14
+ font-family: Arial, sans-serif;
15
+ font-size: 17px;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ padding: 30px 0;
20
+ }
21
+
22
+ .box {
23
+ width: 450px;
24
+ min-height: 500px;
25
+ max-height: 100%;
26
+ padding: 78px 40px 28px 40px;
27
+ margin: 1em;
28
+ border: 1px solid #dadce0;
29
+ border-radius: 8px;
30
+ position: relative;
31
+ display: flex;
32
+ flex-direction: column;
33
+ align-items: center;
34
+ }
35
+
36
+ .box:before {
37
+ width: 100%;
38
+ height: 34px;
39
+ border-bottom: 1px solid #dadce0;
40
+ position: absolute;
41
+ top: 0;
42
+ display: block;
43
+ content: '';
44
+ }
45
+
46
+ ul {
47
+ width: 100%;
48
+ margin: 23px 0 30px 0;
49
+ padding-left: 0;
50
+ list-style-type: none;
51
+ overflow: auto;
52
+ }
53
+
54
+ li {
55
+ line-height: 48px;
56
+ color: brown;
57
+ border-bottom: 1px solid #dadce0;
58
+ display: flex;
59
+ align-items: center;
60
+ }
61
+
62
+ li:before {
63
+ content: 'W';
64
+ width: 28px;
65
+ height: 28px;
66
+ border-radius: 50%;
67
+ border: 2px solid currentColor;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ font-size: 14px;
72
+ margin-right: 10px;
73
+ }
74
+
75
+ li.readonly {
76
+ color: darkgreen;
77
+ }
78
+
79
+ li.readonly:before {
80
+ content: 'R';
81
+ }
82
+
83
+ a {
84
+ width: 50%;
85
+ line-height: 2em;
86
+ margin: auto;
87
+ color: #ffffff;
88
+ background: #0057e7;
89
+ border-radius: 10em;
90
+ display: block;
91
+ text-align: center;
92
+ text-decoration: none;
93
+ }
94
+
95
+ h1 {
96
+ font-size: 24px;
97
+ line-height: 48px;
98
+ font-weight: normal;
99
+ margin: 0;
100
+ }
101
+
102
+ p {
103
+ line-height: 24px;
104
+ margin: 0;
105
+ }
106
+ </style>
107
+ <div class="box">
108
+ ${content}
109
+ </div>
@@ -0,0 +1 @@
1
+ <li class="${type}">${name}</li>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anmiles/google-api-wrapper",
3
- "version": "14.1.0",
3
+ "version": "14.2.0",
4
4
  "description": "Provides quick interface for getting google API data",
5
5
  "keywords": [
6
6
  "google",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "main": "dist/index.js",
18
18
  "scripts": {
19
- "build": "rimraf dist && tsc",
19
+ "build": "rimraf dist && tsc && copyfiles -u 1 src/templates/* dist/",
20
20
  "lint": "eslint --ext .js,.ts .",
21
21
  "lint:fix": "npm run lint -- --fix",
22
22
  "test": "jest --verbose",
@@ -29,6 +29,7 @@
29
29
  "login": "node ./dist/login.js"
30
30
  },
31
31
  "dependencies": {
32
+ "@anmiles/google-api-wrapper": "file:.",
32
33
  "@anmiles/logger": "^3.0.0",
33
34
  "@anmiles/prototypes": "^2.0.1",
34
35
  "@anmiles/sleep": "^1.0.2",
@@ -44,6 +45,7 @@
44
45
  "@types/server-destroy": "^1.0.1",
45
46
  "@typescript-eslint/eslint-plugin": "^5.59.2",
46
47
  "@typescript-eslint/parser": "^5.59.2",
48
+ "copyfiles": "^2.4.1",
47
49
  "eslint": "^8.40.0",
48
50
  "eslint-plugin-align-assignments": "^1.1.2",
49
51
  "eslint-plugin-import": "^2.27.5",
@@ -8,6 +8,7 @@ jest.mock<typeof paths>('../paths', () => ({
8
8
  getScopesFile : jest.fn().mockImplementation(() => scopesFile),
9
9
  getSecretsFile : jest.fn().mockImplementation(() => secretsFile),
10
10
  getCredentialsFile : jest.fn().mockImplementation(() => credentialsFile),
11
+ getTemplateFile : jest.fn().mockImplementation(() => templateFile),
11
12
  }));
12
13
 
13
14
  jest.mock<Partial<typeof fs>>('fs', () => ({
@@ -17,15 +18,17 @@ jest.mock<Partial<typeof fs>>('fs', () => ({
17
18
  }));
18
19
 
19
20
  jest.mock<Partial<typeof path>>('path', () => ({
20
- join : jest.fn().mockImplementation((...args) => args.join('/')),
21
- dirname : jest.fn().mockImplementation((arg) => arg.split('/').slice(0, -1).join('/')),
21
+ join : jest.fn().mockImplementation((...args) => args.join('/')),
22
22
  }));
23
23
 
24
- const profile = 'username';
24
+ const profile = 'username';
25
+ const templateName = 'auth';
26
+
25
27
  const profilesFile = 'input/profiles.json';
26
28
  const scopesFile = 'scopes.json';
27
29
  const secretsFile = 'secrets/username.json';
28
30
  const credentialsFile = 'secrets/username.credentials.json';
31
+ const templateFile = 'node_modules/@anmiles/google-api-wrapper/dist/templates/auth.html';
29
32
 
30
33
  let exists: boolean;
31
34
 
@@ -61,4 +64,13 @@ describe('src/lib/paths', () => {
61
64
  expect(result).toEqual(credentialsFile);
62
65
  });
63
66
  });
67
+
68
+ describe('getTemplateFile', () => {
69
+
70
+ it('should return credentials file', () => {
71
+ const result = original.getTemplateFile(templateName);
72
+
73
+ expect(result).toEqual(templateFile);
74
+ });
75
+ });
64
76
  });
@@ -0,0 +1,108 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import paths from '../paths';
4
+ import renderer from '../renderer';
5
+
6
+ const original = jest.requireActual('../renderer').default as typeof renderer;
7
+
8
+ type TemplateName = keyof typeof original.templates;
9
+ type GetFileName<T extends TemplateName> = `${T}.html`;
10
+
11
+ type FileName = GetFileName<TemplateName>;
12
+ type GetTemplateName<F> = F extends `${infer T}.html` ? T : never;
13
+
14
+ function getFileName<T extends TemplateName>(templateName: T): GetFileName<T> {
15
+ return `${templateName}.html`;
16
+ }
17
+
18
+ function getTemplateName<F extends FileName>(file: F): GetTemplateName<F> {
19
+ return file.replace('.html', '') as GetTemplateName<F>;
20
+ }
21
+
22
+ jest.mock<Partial<typeof renderer>>('../renderer', () => ({
23
+ getTemplate : jest.fn().mockImplementation((templateName: TemplateName) => mockTemplates[templateName]),
24
+ }));
25
+
26
+ jest.mock<Partial<typeof fs>>('fs', () => ({
27
+ readFileSync : jest.fn().mockImplementation((file: FileName) => mockTemplates[getTemplateName(file)] || ''),
28
+ }));
29
+
30
+ jest.mock<Partial<typeof path>>('path', () => ({
31
+ join : jest.fn().mockImplementation((...args) => args.join('/')),
32
+ }));
33
+
34
+ jest.mock<Partial<typeof paths>>('../paths', () => ({
35
+ getTemplateFile : jest.fn().mockImplementation((templateName) => getFileName(templateName)),
36
+ }));
37
+
38
+ const mockTemplates: Record<TemplateName, string> = {
39
+ page : 'page content = (${content})',
40
+ auth : 'auth profile = (${profile}) authUrl = (${authUrl}) scopesList = (${scopesList})',
41
+ scope : 'scope type = (${type}) name = (${name})',
42
+ done : 'done profile = (${profile})',
43
+ };
44
+
45
+ const authUrl = 'https://authUrl';
46
+ const profile = 'username';
47
+ const scope = [ 'namespace/scope1.readonly', 'namespace/scope2' ];
48
+
49
+ describe('src/lib/renderer', () => {
50
+ describe('renderAuth', () => {
51
+ it('should return auth page', () => {
52
+ const result = original.renderAuth({ authUrl, profile, scope });
53
+ expect(result).toEqual('page content = (auth profile = (username) authUrl = (https://authUrl) scopesList = (scope type = (readonly) name = (scope1.readonly)\nscope type = () name = (scope2)))');
54
+ });
55
+ });
56
+
57
+ describe('renderDone', () => {
58
+ it('should return done page', () => {
59
+ const result = original.renderDone({ profile });
60
+ expect(result).toEqual('page content = (done profile = (username))');
61
+ });
62
+ });
63
+
64
+ describe('render', () => {
65
+ it('should replace all template variables with their values', () => {
66
+ const values = { type : 'readonly', name : 'scope.readonly' };
67
+
68
+ const result = original.render('scope', values);
69
+
70
+ expect(result).toEqual('scope type = (readonly) name = (scope.readonly)');
71
+ });
72
+
73
+ it('should replace non-existing template variable with empty string', () => {
74
+ const values = { name : 'scope.readonly' };
75
+
76
+ const result = original.render('scope', values as any);
77
+
78
+ expect(result).toEqual('scope type = () name = (scope.readonly)');
79
+ });
80
+ });
81
+
82
+ describe('getTemplate', () => {
83
+ for (const templateName in original.templates) {
84
+ it(`should return contents of '${templateName}' template file`, () => {
85
+ const template = original.getTemplate(templateName as TemplateName);
86
+ expect(fs.readFileSync).toHaveBeenCalledTimes(1);
87
+ expect(fs.readFileSync).toHaveBeenCalledWith(`${templateName}.html`);
88
+ expect(template).toEqual(mockTemplates[templateName as TemplateName]);
89
+ });
90
+ }
91
+
92
+ it('should cache templates', () => {
93
+ original.getTemplate('template1' as TemplateName);
94
+
95
+ original.getTemplate('template2' as TemplateName);
96
+ original.getTemplate('template2' as TemplateName);
97
+ original.getTemplate('template2' as TemplateName);
98
+
99
+ original.getTemplate('template3' as TemplateName);
100
+
101
+ expect(fs.readFileSync).toHaveBeenCalledWith('template1.html');
102
+ expect(fs.readFileSync).toHaveBeenCalledWith('template2.html');
103
+ expect(fs.readFileSync).toHaveBeenCalledWith('template3.html');
104
+
105
+ expect(fs.readFileSync).toHaveBeenCalledTimes(3);
106
+ });
107
+ });
108
+ });
@@ -5,6 +5,7 @@ import open from 'open';
5
5
  import type GoogleApis from 'googleapis';
6
6
  import logger from '@anmiles/logger';
7
7
  import emitter from 'event-emitter';
8
+ import renderer from '../renderer';
8
9
  import paths from '../paths';
9
10
  import type { Secrets } from '../../types';
10
11
  import '@anmiles/prototypes';
@@ -23,6 +24,11 @@ jest.mock<typeof secrets>('../secrets', () => ({
23
24
  getSecretsError : jest.fn().mockImplementation(() => secretsError),
24
25
  }));
25
26
 
27
+ jest.mock<Partial<typeof renderer>>('../renderer', () => ({
28
+ renderAuth : jest.fn().mockImplementation(({ profile, authUrl, scope } : { profile: string, authUrl: string, scope: string[] }) => `content = profile = ${profile} authUrl = ${authUrl} scope = ${scope.join('|')}`),
29
+ renderDone : jest.fn().mockImplementation(() => 'content = done'),
30
+ }));
31
+
26
32
  jest.mock<Partial<typeof http>>('http', () => ({
27
33
  createServer : jest.fn().mockImplementation(() => server),
28
34
  }));
@@ -60,6 +66,7 @@ jest.mock<Partial<typeof paths>>('../paths', () => ({
60
66
  getScopesFile : jest.fn().mockImplementation(() => scopesFile),
61
67
  getSecretsFile : jest.fn().mockImplementation(() => secretsFile),
62
68
  getCredentialsFile : jest.fn().mockImplementation(() => credentialsFile),
69
+ getTemplateFile : jest.fn().mockImplementation(() => templateFile),
63
70
  }));
64
71
 
65
72
  jest.useFakeTimers();
@@ -72,6 +79,7 @@ const profile = 'username1';
72
79
  const scopesFile = 'scopes.json';
73
80
  const secretsFile = 'secrets/username1.json';
74
81
  const credentialsFile = 'secrets/username1.credentials.json';
82
+ const templateFile = 'templates/template.json';
75
83
  const wrongRedirectURI = 'wrong_redirect_uri';
76
84
 
77
85
  const scopesError = 'scopesError';
@@ -79,7 +87,7 @@ const secretsError = 'secretsError';
79
87
 
80
88
  const scopesJSON: string[] = [
81
89
  'https://www.googleapis.com/auth/calendar.calendars.readonly',
82
- 'https://www.googleapis.com/auth/calendar.events.readonly',
90
+ 'https://www.googleapis.com/auth/calendar.events',
83
91
  ];
84
92
 
85
93
  const secretsJSON: Secrets = {
@@ -371,7 +379,7 @@ describe('src/lib/secrets', () => {
371
379
  prompt : undefined,
372
380
  scope : [
373
381
  'https://www.googleapis.com/auth/calendar.calendars.readonly',
374
- 'https://www.googleapis.com/auth/calendar.events.readonly',
382
+ 'https://www.googleapis.com/auth/calendar.events',
375
383
  ],
376
384
  });
377
385
  });
@@ -386,7 +394,7 @@ describe('src/lib/secrets', () => {
386
394
  prompt : 'consent',
387
395
  scope : [
388
396
  'https://www.googleapis.com/auth/calendar.calendars.readonly',
389
- 'https://www.googleapis.com/auth/calendar.events.readonly',
397
+ 'https://www.googleapis.com/auth/calendar.events',
390
398
  ],
391
399
  });
392
400
  });
@@ -442,14 +450,7 @@ describe('src/lib/secrets', () => {
442
450
  makeRequest('/');
443
451
  await Promise.resolve();
444
452
 
445
- expect(endSpy).toHaveBeenCalledWith(`\
446
- <div style="width: 100%;height: 100%;display: flex;align-items: start;justify-content: center">\n\
447
- <div style="padding: 0 1em;border: 1px solid black;font-family: Arial, sans-serif;margin: 1em;">\n\
448
- <p>Please open <a href="${authUrl}">auth page</a> using <strong>${profile}</strong> google profile</p>\n\
449
- <p>Required scopes:</p>\n\
450
- <ul><li>https://www.googleapis.com/auth/calendar.calendars.readonly</li><li>https://www.googleapis.com/auth/calendar.events.readonly</li></ul>\n\
451
- </div>\n\
452
- </div>`);
453
+ expect(endSpy).toHaveBeenCalledWith('content = profile = username1 authUrl = https://authUrl scope = https://www.googleapis.com/auth/calendar.calendars.readonly|https://www.googleapis.com/auth/calendar.events');
453
454
  });
454
455
 
455
456
  it('should ask to close webpage', async () => {
@@ -457,12 +458,7 @@ describe('src/lib/secrets', () => {
457
458
  makeRequest(tokenUrl);
458
459
  await Promise.resolve();
459
460
 
460
- expect(endSpy).toHaveBeenCalledWith('\
461
- <div style="width: 100%;height: 100%;display: flex;align-items: start;justify-content: center">\n\
462
- <div style="padding: 0 1em;border: 1px solid black;font-family: Arial, sans-serif;margin: 1em;">\n\
463
- <p>Please close this page and return to application</p>\n\
464
- </div>\n\
465
- </div>');
461
+ expect(endSpy).toHaveBeenCalledWith('content = done');
466
462
  });
467
463
 
468
464
  it('should close server and destroy all connections if request.url is truthy', async () => {
@@ -577,7 +573,7 @@ Here is how to obtain it:\n\
577
573
  Specify your email as user support email and as developer contact information on the very bottom\n\
578
574
  Click "Save and continue"\n\
579
575
  Click "Add or remove scopes"\n\
580
- Add scopes: https://www.googleapis.com/auth/calendar.calendars.readonly,https://www.googleapis.com/auth/calendar.events.readonly\n\
576
+ Add scopes: https://www.googleapis.com/auth/calendar.calendars.readonly,https://www.googleapis.com/auth/calendar.events\n\
581
577
  Click "Save and continue"\n\
582
578
  Click "Add users"\n\
583
579
  Add your email\n\
package/src/lib/paths.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  import path from 'path';
2
+ import type { templates } from './renderer';
2
3
 
3
- export { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile };
4
- export default { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile };
4
+ export { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile, getTemplateFile };
5
+ export default { getProfilesFile, getScopesFile, getSecretsFile, getCredentialsFile, getTemplateFile };
5
6
 
6
7
  const dirPaths = {
7
- input : 'input',
8
- secrets : 'secrets',
8
+ input : 'input',
9
+ secrets : 'secrets',
10
+ // TODO: Remove this hack after moving to React
11
+ templates : 'node_modules/@anmiles/google-api-wrapper/dist/templates',
9
12
  };
10
13
 
11
14
  function getProfilesFile() {
@@ -23,3 +26,7 @@ function getSecretsFile(profile: string) {
23
26
  function getCredentialsFile(profile: string) {
24
27
  return path.join(dirPaths.secrets, `${profile}.credentials.json`);
25
28
  }
29
+
30
+ function getTemplateFile(templateName: keyof typeof templates) {
31
+ return path.join(dirPaths.templates, `${templateName}.html`);
32
+ }
@@ -0,0 +1,56 @@
1
+ import fs from 'fs';
2
+ import renderer from './renderer';
3
+ import { getTemplateFile } from './paths';
4
+
5
+ export { templates, renderAuth, renderDone };
6
+
7
+ const templates = {
8
+ page : [ 'content' ] as const,
9
+ auth : [ 'profile', 'authUrl', 'scopesList' ],
10
+ scope : [ 'type', 'name' ] as const,
11
+ done : [ 'profile' ] as const,
12
+ } as const;
13
+
14
+ type TemplateName = keyof typeof templates;
15
+
16
+ const allHTML = {} as Record<TemplateName, string>;
17
+
18
+ function renderAuth({ profile, authUrl, scope }: { profile: string, authUrl: string, scope: string[] }) {
19
+ const scopesList = scope.map((s) => render('scope', {
20
+ name : s.split('/').pop() as string,
21
+ type : s.endsWith('.readonly') ? 'readonly' : '',
22
+ })).join('\n');
23
+
24
+ const content = render('auth', { profile, authUrl, scopesList });
25
+ return render('page', { content });
26
+ }
27
+
28
+ function renderDone({ profile }: { profile: string }): string {
29
+ const content = render('done', { profile });
30
+ return render('page', { content });
31
+ }
32
+
33
+ // TODO: Use react
34
+ function render<T extends TemplateName>(templateName: T, values: Record<typeof templates[T][number], string>): string {
35
+ let html = renderer.getTemplate(templateName);
36
+ const allValues = values as Record<typeof templates[TemplateName][number], string>;
37
+
38
+ for (const variable of templates[templateName]) {
39
+ const value = allValues[variable] || '';
40
+ html = html.replace(`\${${variable}}`, value);
41
+ }
42
+
43
+ return html;
44
+ }
45
+
46
+ function getTemplate(templateName: TemplateName) {
47
+ if (!(templateName in allHTML)) {
48
+ const file = getTemplateFile(templateName);
49
+ const template = fs.readFileSync(file).toString();
50
+ allHTML[templateName] = template;
51
+ }
52
+
53
+ return allHTML[templateName];
54
+ }
55
+
56
+ export default { templates, render, getTemplate, renderAuth, renderDone };
@@ -7,6 +7,7 @@ import { warn } from '@anmiles/logger';
7
7
  import type { Secrets, AuthOptions } from '../types';
8
8
  import '@anmiles/prototypes';
9
9
  import { getScopesFile, getSecretsFile, getCredentialsFile } from './paths';
10
+ import { renderAuth, renderDone } from './renderer';
10
11
 
11
12
  import secrets from './secrets';
12
13
 
@@ -96,12 +97,11 @@ async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Cl
96
97
  const code = url.searchParams.get('code');
97
98
 
98
99
  if (!code) {
99
- const scopesList = scope.map((s) => `<li>${s}</li>`).join('');
100
- response.end(formatMessage(`<p>Please open <a href="${authUrl}">auth page</a> using <strong>${profile}</strong> google profile</p>\n<p>Required scopes:</p>\n<ul>${scopesList}</ul>`));
100
+ response.end(renderAuth({ profile, authUrl, scope }));
101
101
  return;
102
102
  }
103
103
 
104
- response.end(formatMessage('<p>Please close this page and return to application</p>'));
104
+ response.end(renderDone({ profile }));
105
105
  server.destroy();
106
106
  const { tokens } = await auth.getToken(code);
107
107
  resolve(tokens);
@@ -132,16 +132,6 @@ function deleteCredentials(profile: string): void {
132
132
  }
133
133
  }
134
134
 
135
- function formatMessage(message: string): string {
136
- return [
137
- '<div style="width: 100%;height: 100%;display: flex;align-items: start;justify-content: center">',
138
- '<div style="padding: 0 1em;border: 1px solid black;font-family: Arial, sans-serif;margin: 1em;">',
139
- message,
140
- '</div>',
141
- '</div>',
142
- ].join('\n');
143
- }
144
-
145
135
  function checkSecrets(profile: string, secretsObject: Secrets, secretsFile: string): true | void {
146
136
  if (secretsObject.web.redirect_uris[0] === callbackURI) {
147
137
  return true;
@@ -0,0 +1,6 @@
1
+ <h1>Welcome ${profile}!</h1>
2
+ <p>Please authorize:</p>
3
+ <ul>
4
+ ${scopesList}
5
+ </ul>
6
+ <a href="${authUrl}">Continue</a>
@@ -0,0 +1,3 @@
1
+ <h1>Welcome ${profile}!</h1>
2
+ <p>Authorization succeeded.</p>
3
+ <p>Please close this page and return to application.</p>
@@ -0,0 +1,109 @@
1
+ <style type="text/css">
2
+ * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ html, body {
7
+ width: 100%;
8
+ height: 100%;
9
+ margin: 0;
10
+ padding: 0;
11
+ }
12
+
13
+ body {
14
+ font-family: Arial, sans-serif;
15
+ font-size: 17px;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ padding: 30px 0;
20
+ }
21
+
22
+ .box {
23
+ width: 450px;
24
+ min-height: 500px;
25
+ max-height: 100%;
26
+ padding: 78px 40px 28px 40px;
27
+ margin: 1em;
28
+ border: 1px solid #dadce0;
29
+ border-radius: 8px;
30
+ position: relative;
31
+ display: flex;
32
+ flex-direction: column;
33
+ align-items: center;
34
+ }
35
+
36
+ .box:before {
37
+ width: 100%;
38
+ height: 34px;
39
+ border-bottom: 1px solid #dadce0;
40
+ position: absolute;
41
+ top: 0;
42
+ display: block;
43
+ content: '';
44
+ }
45
+
46
+ ul {
47
+ width: 100%;
48
+ margin: 23px 0 30px 0;
49
+ padding-left: 0;
50
+ list-style-type: none;
51
+ overflow: auto;
52
+ }
53
+
54
+ li {
55
+ line-height: 48px;
56
+ color: brown;
57
+ border-bottom: 1px solid #dadce0;
58
+ display: flex;
59
+ align-items: center;
60
+ }
61
+
62
+ li:before {
63
+ content: 'W';
64
+ width: 28px;
65
+ height: 28px;
66
+ border-radius: 50%;
67
+ border: 2px solid currentColor;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ font-size: 14px;
72
+ margin-right: 10px;
73
+ }
74
+
75
+ li.readonly {
76
+ color: darkgreen;
77
+ }
78
+
79
+ li.readonly:before {
80
+ content: 'R';
81
+ }
82
+
83
+ a {
84
+ width: 50%;
85
+ line-height: 2em;
86
+ margin: auto;
87
+ color: #ffffff;
88
+ background: #0057e7;
89
+ border-radius: 10em;
90
+ display: block;
91
+ text-align: center;
92
+ text-decoration: none;
93
+ }
94
+
95
+ h1 {
96
+ font-size: 24px;
97
+ line-height: 48px;
98
+ font-weight: normal;
99
+ margin: 0;
100
+ }
101
+
102
+ p {
103
+ line-height: 24px;
104
+ margin: 0;
105
+ }
106
+ </style>
107
+ <div class="box">
108
+ ${content}
109
+ </div>
@@ -0,0 +1 @@
1
+ <li class="${type}">${name}</li>