resend 0.1.1 → 0.8.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.
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "resend",
3
+ "version": "0.8.0",
4
+ "description": "",
5
+ "main": "build/src/index.js",
6
+ "typings": "build/src/index.d.ts",
7
+ "files": [
8
+ "build/"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc -p .",
12
+ "lint": "tslint -p tsconfig.json -c tslint.json",
13
+ "test": "jest",
14
+ "test:watch": "jest --watch",
15
+ "prettier": "prettier \"src/**/*.{js,ts,tsx}\" --check",
16
+ "format": "prettier \"src/**/*.{js,ts,tsx}\" --write",
17
+ "prepublishOnly": "yarn run build"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/resend-inc/resend-node.git"
22
+ },
23
+ "author": "",
24
+ "license": "MIT",
25
+ "bugs": {
26
+ "url": "https://github.com/resend-inc/resend-node/issues"
27
+ },
28
+ "homepage": "https://github.com/resend-inc/resend-node#readme",
29
+ "dependencies": {
30
+ "@react-email/render": "^0.0.1",
31
+ "axios": "0.27.2"
32
+ },
33
+ "devDependencies": {
34
+ "@types/jest": "28.1.7",
35
+ "@types/node": "18.6.4",
36
+ "@types/react": "18.0.17",
37
+ "axios-mock-adapter": "1.21.2",
38
+ "jest": "28.1.3",
39
+ "prettier": "2.7.1",
40
+ "supertest": "6.2.4",
41
+ "ts-jest": "28.0.8",
42
+ "tslint": "6.1.3",
43
+ "typescript": "4.7.4"
44
+ }
45
+ }
@@ -0,0 +1,2 @@
1
+ import { Resend } from './resend';
2
+ export { Resend };
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Resend = void 0;
4
+ const resend_1 = require("./resend");
5
+ Object.defineProperty(exports, "Resend", { enumerable: true, get: function () { return resend_1.Resend; } });
@@ -0,0 +1,19 @@
1
+ import { ReactElement } from 'react';
2
+ export interface SendEmailData {
3
+ /** Sender email address. */
4
+ from: string;
5
+ /** Recipient email address. Max 50. */
6
+ to: string | string[];
7
+ /** Bcc recipient email address. Max 50. */
8
+ bcc?: string | string[];
9
+ /** Cc recipient email address. Max 50. */
10
+ cc?: string | string[];
11
+ /** Email subject. */
12
+ subject?: string;
13
+ /** The HTML version of the message. */
14
+ text?: string;
15
+ /** The plain text version of the message. */
16
+ html?: string;
17
+ /** The React version of the message. */
18
+ react?: ReactElement;
19
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,9 @@
1
+ import { SendEmailData } from './interfaces';
2
+ export declare class Resend {
3
+ readonly apiKey?: string | undefined;
4
+ readonly baseUrl: string;
5
+ private readonly headers;
6
+ private readonly request;
7
+ constructor(apiKey?: string | undefined);
8
+ sendEmail(data: SendEmailData): Promise<object>;
9
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.Resend = void 0;
16
+ const axios_1 = __importDefault(require("axios"));
17
+ const render_1 = require("@react-email/render");
18
+ const package_json_1 = require("../package.json");
19
+ class Resend {
20
+ constructor(apiKey) {
21
+ this.apiKey = apiKey;
22
+ if (!apiKey) {
23
+ this.apiKey = process.env.RESEND_API_KEY;
24
+ if (!this.apiKey) {
25
+ throw new Error('Missing API key. Pass it to the constructor `new Resend("re_123")`');
26
+ }
27
+ }
28
+ this.apiKey = apiKey;
29
+ this.baseUrl = process.env.RESEND_BASE_URL || 'https://api.resend.com';
30
+ this.headers = {
31
+ Authorization: `Bearer ${this.apiKey}`,
32
+ 'User-Agent': `node:${package_json_1.version}`,
33
+ 'Content-Type': 'application/json',
34
+ };
35
+ this.request = axios_1.default.create({
36
+ baseURL: this.baseUrl,
37
+ headers: this.headers,
38
+ });
39
+ }
40
+ sendEmail(data) {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ try {
43
+ const path = `${this.baseUrl}/email`;
44
+ if (data.react) {
45
+ data.html = (0, render_1.render)(data.react);
46
+ delete data.react;
47
+ }
48
+ const response = yield this.request(path, { method: 'POST', data });
49
+ return response.data;
50
+ }
51
+ catch (error) {
52
+ throw error;
53
+ }
54
+ });
55
+ }
56
+ }
57
+ exports.Resend = Resend;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const resend_1 = require("./resend");
16
+ const axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
17
+ const axios_1 = __importDefault(require("axios"));
18
+ const mock = new axios_mock_adapter_1.default(axios_1.default);
19
+ const resend = new resend_1.Resend('re_924b3rjh2387fbewf823');
20
+ describe('Resend', () => {
21
+ afterEach(() => mock.resetHistory());
22
+ it('throws API key error', () => {
23
+ expect(() => new resend_1.Resend()).toThrowErrorMatchingSnapshot();
24
+ });
25
+ it('sends email', () => __awaiter(void 0, void 0, void 0, function* () {
26
+ const payload = {
27
+ from: 'bu@resend.com',
28
+ to: 'zeno@resend.com',
29
+ subject: 'Hello World',
30
+ };
31
+ mock.onPost('https://api.resend.com/email', payload).replyOnce(200, {
32
+ id: '1234',
33
+ from: 'bu@resend.com',
34
+ to: 'zeno@resend.com',
35
+ created_at: '123',
36
+ });
37
+ const data = yield resend.sendEmail(payload);
38
+ expect(data).toMatchSnapshot();
39
+ }));
40
+ it('sends email with multiple recipients', () => __awaiter(void 0, void 0, void 0, function* () {
41
+ const payload = {
42
+ from: 'admin@resend.com',
43
+ to: ['bu@resend.com', 'zeno@resend.com'],
44
+ subject: 'Hello World',
45
+ };
46
+ mock.onPost('https://api.resend.com/email', payload).replyOnce(200, {
47
+ id: '1234',
48
+ from: 'admin@resend.com',
49
+ to: ['bu@resend.com', 'zeno@resend.com'],
50
+ created_at: '123',
51
+ });
52
+ const data = yield resend.sendEmail(payload);
53
+ expect(data).toMatchSnapshot();
54
+ }));
55
+ it('sends email with multiple bcc recipients', () => __awaiter(void 0, void 0, void 0, function* () {
56
+ const payload = {
57
+ from: 'admin@resend.com',
58
+ to: 'bu@resend.com',
59
+ bcc: ['foo@resend.com', 'bar@resend.com'],
60
+ subject: 'Hello World',
61
+ };
62
+ mock.onPost('https://api.resend.com/email', payload).replyOnce(200, {
63
+ id: '1234',
64
+ from: 'admin@resend.com',
65
+ to: 'bu@resend.com',
66
+ bcc: ['foo@resend.com', 'bar@resend.com'],
67
+ created_at: '123',
68
+ });
69
+ const data = yield resend.sendEmail(payload);
70
+ expect(data).toMatchSnapshot();
71
+ }));
72
+ it('sends email with multiple cc recipients', () => __awaiter(void 0, void 0, void 0, function* () {
73
+ const payload = {
74
+ from: 'admin@resend.com',
75
+ to: 'bu@resend.com',
76
+ cc: ['foo@resend.com', 'bar@resend.com'],
77
+ subject: 'Hello World',
78
+ };
79
+ mock.onPost('https://api.resend.com/email', payload).replyOnce(200, {
80
+ id: '1234',
81
+ from: 'admin@resend.com',
82
+ to: 'bu@resend.com',
83
+ cc: ['foo@resend.com', 'bar@resend.com'],
84
+ created_at: '123',
85
+ });
86
+ const data = yield resend.sendEmail(payload);
87
+ expect(data).toMatchSnapshot();
88
+ }));
89
+ });
package/package.json CHANGED
@@ -1,26 +1,45 @@
1
1
  {
2
2
  "name": "resend",
3
- "version": "0.1.1",
4
- "description": "Web server for Redis data including Refile support",
5
- "main": "lib/index.js",
3
+ "version": "0.8.0",
4
+ "description": "",
5
+ "main": "build/src/index.js",
6
+ "typings": "build/src/index.d.ts",
7
+ "files": [
8
+ "build/"
9
+ ],
6
10
  "scripts": {
7
- "lint": "eslint --cache src",
8
- "start": "node --harmony lib/index.js"
9
- },
10
- "author": "@evanxsummers",
11
- "license": "ISC",
12
- "dependencies": {
13
- "redis-koa-app-rpf": "^0.6.5"
14
- },
15
- "devDependencies": {
16
- "eslint": "^3.12.2"
11
+ "build": "tsc -p .",
12
+ "lint": "tslint -p tsconfig.json -c tslint.json",
13
+ "test": "jest",
14
+ "test:watch": "jest --watch",
15
+ "prettier": "prettier \"src/**/*.{js,ts,tsx}\" --check",
16
+ "format": "prettier \"src/**/*.{js,ts,tsx}\" --write",
17
+ "prepublishOnly": "yarn run build"
17
18
  },
18
19
  "repository": {
19
20
  "type": "git",
20
- "url": "git+https://github.com/evanx/resend.git"
21
+ "url": "git+https://github.com/resend-inc/resend-node.git"
21
22
  },
23
+ "author": "",
24
+ "license": "MIT",
22
25
  "bugs": {
23
- "url": "https://github.com/evanx/resend/issues"
26
+ "url": "https://github.com/resend-inc/resend-node/issues"
24
27
  },
25
- "homepage": "https://github.com/evanx/resend#readme"
26
- }
28
+ "homepage": "https://github.com/resend-inc/resend-node#readme",
29
+ "dependencies": {
30
+ "@react-email/render": "^0.0.1",
31
+ "axios": "0.27.2"
32
+ },
33
+ "devDependencies": {
34
+ "@types/jest": "28.1.7",
35
+ "@types/node": "18.6.4",
36
+ "@types/react": "18.0.17",
37
+ "axios-mock-adapter": "1.21.2",
38
+ "jest": "28.1.3",
39
+ "prettier": "2.7.1",
40
+ "supertest": "6.2.4",
41
+ "ts-jest": "28.0.8",
42
+ "tslint": "6.1.3",
43
+ "typescript": "4.7.4"
44
+ }
45
+ }
package/readme.md ADDED
@@ -0,0 +1,82 @@
1
+ # Resend Node.js SDK
2
+
3
+ Node.js library for the Resend API.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install resend
9
+ # or
10
+ yarn add resend
11
+ ```
12
+
13
+ ## Setup
14
+
15
+ First, you need to get an API key, which is available in the [Resend Dashboard](https://resend.com).
16
+
17
+ ```js
18
+ import { Resend } from 'resend';
19
+ const resend = new Resend('re_123');
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Send your first email:
25
+
26
+ ```js
27
+ await resend.sendEmail({
28
+ from: 'you@example.com',
29
+ to: 'user@gmail.com',
30
+ subject: 'hello world',
31
+ text: 'it works!',
32
+ });
33
+ ```
34
+
35
+ ## Send email using HTML
36
+
37
+ Send an email custom HTML content:
38
+
39
+ ```js
40
+ await resend.sendEmail({
41
+ from: 'you@example.com',
42
+ to: 'user@gmail.com',
43
+ subject: 'hello world',
44
+ html: '<strong>it works!</strong>',
45
+ });
46
+ ```
47
+
48
+ ## Send email using React
49
+
50
+ Start by creating your email template as a React component.
51
+
52
+ ```jsx
53
+ import React from 'react'
54
+
55
+ export default function EmailTemplate(props) {
56
+ const { firstName, product } = props
57
+
58
+ return (
59
+ <div>
60
+ <h1>Welcome, {firstName}!</h1>
61
+ <p>Thanks for trying {product}. We’re thrilled to have you on board.</p>
62
+ </div>
63
+ )
64
+ }
65
+ ```
66
+
67
+ Then import the template component and pass it to the `react` property.
68
+
69
+ ```jsx
70
+ import EmailTemplate from '../components/EmailTemplate'
71
+
72
+ await resend.sendEmail({
73
+ from: 'you@example.com',
74
+ to: 'user@gmail.com',
75
+ subject: 'hello world',
76
+ react: <EmailTemplate firstName="John" product="MyApp" />,
77
+ });
78
+ ```
79
+
80
+ ## License
81
+
82
+ MIT License
package/.eslintrc DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "parserOptions": {
3
- "ecmaVersion": 2017,
4
- "sourceType": "module"
5
- }
6
- }
package/.npmignore DELETED
@@ -1,7 +0,0 @@
1
- /node_modules
2
- /npm-debug.log
3
- /config
4
- /tmp
5
- /.eslintcache
6
- /.idea
7
- /.vscode
package/Dockerfile DELETED
@@ -1,5 +0,0 @@
1
- FROM mhart/alpine-node
2
- ADD package.json .
3
- RUN npm install
4
- ADD lib lib
5
- CMD ["node", "--harmony", "lib/index.js"]
package/README.md DELETED
@@ -1,74 +0,0 @@
1
- # resend
2
-
3
- Containerized utility to scan and print llen of Redis keys.
4
-
5
- <img src="https://raw.githubusercontent.com/evanx/resend/master/docs/readme/main.png"/>
6
-
7
- ## Use case
8
-
9
- We wish to expire a set of keys in Redis matching some pattern.
10
-
11
- ```
12
- ```
13
-
14
- ## Config
15
-
16
- See `lib/config.js`
17
- ```javascript
18
- ```
19
-
20
- ## Docker
21
-
22
- You can build as follows:
23
- ```shell
24
- docker build -t resend https://github.com/evanx/resend.git
25
- ```
26
-
27
- See `bin/test.sh` https://github.com/evanx/resend/blob/master/bin/test.sh
28
-
29
- Builds:
30
- - isolated network `resend-network`
31
- - isolated Redis instance named `resend-redis`
32
- - this utility `evanx/resend`
33
-
34
- We populate our test keys:
35
- ```
36
- populate() {
37
- }
38
- ```
39
-
40
- We build a container image for this service:
41
- ```
42
- docker build -t resend https://github.com/evanx/resend.git
43
- ```
44
-
45
- We interactively run the service on our test Redis container:
46
- ```
47
- docker run --name resend-instance --rm -i \
48
- --network=resend-network \
49
- -e host=$redisHost \
50
- -e pattern='*' \
51
- resend
52
- ```
53
-
54
- ## Implementation
55
-
56
- See `lib/main.js`
57
-
58
- ```javascript
59
- ```
60
-
61
- ### Appication archetype
62
-
63
- Incidently `lib/index.js` uses the `redis-koa-app-rpf` application archetype.
64
- ```
65
- require('redis-koa-app-rpf')(require('./spec'), require('./main'));
66
- ```
67
- where we extract the `config` from `process.env` according to the `spec` and invoke our `main` function.
68
-
69
- See https://github.com/evanx/redis-koa-app-rpf.
70
-
71
- This provides lifecycle boilerplate to reuse across similar applications.
72
-
73
- <hr>
74
- https://twitter.com/@evanxsummers
package/bin/redeploy.sh DELETED
@@ -1,13 +0,0 @@
1
-
2
- echo "NODE_ENV $NODE_ENV"
3
- docker build -t geo-cache https://github.com/evanx/geo-cache.git
4
- docker rm -f `docker ps -q -f name=geo-cache`
5
- container=`docker run --name geo-cache -d \
6
- --network=host --restart unless-stopped \
7
- -e httpPort=8851 \
8
- -e NODE_ENV=$NODE_ENV \
9
- geo-cache`
10
- sleep 1
11
- echo "docker logs $container"
12
- docker logs $container
13
-
@@ -1,9 +0,0 @@
1
-
2
- set -u -e
3
-
4
- echo $# | grep '^0$\|^1$'
5
-
6
- git add -A
7
- git commit -m "${1-initial}"
8
- git push
9
-
@@ -1,24 +0,0 @@
1
-
2
- set -u -x
3
-
4
- mkdir -p tmp
5
-
6
- redis-cli keys cache-geo-cache:* | xargs -n 1 redis-cli del
7
-
8
- echo '
9
- redis-cli keys cache-geo-cache:* | head
10
- redis-cli keys cache-geo-cache:* | wc -l
11
- ' | sed '/^$/d' | tee tmp/keys.sh | dash -x
12
- cat tmp/keys.sh
13
- ls -l tmp/keys.sh
14
-
15
- (
16
- echo 'client sleep'
17
- sleep 1
18
- echo 'client curl'
19
- curl 'http://localhost:8851/maps/api/geocode/json' -G --data-urlencode 'address=20 Falstaff Close, Eynsham OX29 4QA'
20
- redis-cli keys cache-geo-cache:*
21
- ) &
22
-
23
- echo 'start server'
24
- apiKey=$MAPS_API_KEY node --harmony lib/index.js
Binary file
Binary file
package/lib/index.js DELETED
@@ -1,2 +0,0 @@
1
-
2
- require('redis-koa-app-rpf')(require('./spec'), require('./main'));
package/lib/main.js DELETED
@@ -1,132 +0,0 @@
1
- const assert = require('assert');
2
- const crypto = require('crypto');
3
- const fetch = require('node-fetch');
4
- const h = require('render-html-rpf');
5
- const lodash = require('lodash');
6
- const multiExecAsync = require('multi-exec-async');
7
-
8
- const reduceAllProperties = (object, fn) => Object.keys(object).reduce((result, key) => {
9
- result[key] = fn(object[key]);
10
- return result;
11
- }, {});
12
-
13
- module.exports = async appx => {
14
- const {config, logger, client, api} = appx;
15
- logger.info({config}, process.env.NODE_ENV);
16
- api.get('/stats', async ctx => {
17
- ctx.redirect('/metrics');
18
- });
19
- api.get('/metrics', async ctx => {
20
- const [getCountRes, setCountRes, migrateCountRes] = await multiExecAsync(client, multi => {
21
- multi.hgetall([config.redisNamespace, 'metric:get:path:count:h'].join(':'));
22
- multi.hgetall([config.redisNamespace, 'metric:set:path:count:h'].join(':'));
23
- multi.hgetall([config.redisNamespace, 'metric:migrate:path:count:h'].join(':'));
24
- });
25
- const getCount = reduceAllProperties(getCountRes || {}, value => parseInt(value));
26
- const setCount = reduceAllProperties(setCountRes || {}, value => parseInt(value));
27
- const migrateCount = reduceAllProperties(migrateCountRes || {}, value => parseInt(value));
28
- const metrics = {getCount, setCount, migrateCount};
29
- if (/(Mobile)/.test(ctx.get('user-agent'))) {
30
- ctx.body = h.page({
31
- title: 'gcache',
32
- heading: 'Metrics',
33
- content: [{
34
- name: 'pre',
35
- content: JSON.stringify(metrics, null, 2)}
36
- ],
37
- footerLink: 'https://github.com/evanx/geo-cache'
38
- });
39
- } else {
40
- ctx.body = metrics;
41
- }
42
- });
43
- api.get('/maps/api/*', async ctx => {
44
- const path = ctx.params[0];
45
- const url = 'https://maps.googleapis.com/maps/api/' + path;
46
- const query = Object.keys(ctx.query)
47
- .filter(key => key !== 'key')
48
- .reduce((query, key) => {
49
- query[key] = ctx.query[key];
50
- return query;
51
- }, {});
52
- assert(!query.key);
53
- const queryString = Object.keys(query).slice(0).sort().map(
54
- key => [key, encodeURIComponent(query[key])].join('=')
55
- ).join('&');
56
- const urlString = [url, queryString].join('?');
57
- const authQuery = Object.assign({}, {key: config.apiKey}, ctx.query);
58
- if (!authQuery.key) {
59
- ctx.statusCode = 401;
60
- const statusText = 'Unauthorized';
61
- ctx.body = statusText + '\n';
62
- return;
63
- }
64
- logger.debug({url, query, urlString});
65
- const sha = crypto.createHash('sha1').update(urlString).digest('hex');
66
- const cacheKey = [config.redisNamespace, sha, 'j'].join(':');
67
- if (true) {
68
- const migrateSha = crypto.createHash('sha1').update(
69
- [url, JSON.stringify(query)].join('#')
70
- ).digest('hex');
71
- const migrateKey = ['cache-geo-cache', migrateSha, 'json'].join(':');
72
- const [migrateContent] = await multiExecAsync(client, multi => {
73
- multi.get(migrateKey);
74
- });
75
- if (migrateContent) {
76
- await multiExecAsync(client, multi => {
77
- multi.set(cacheKey, JSON.stringify(JSON.parse(migrateContent)));
78
- multi.del(migrateKey);
79
- multi.hincrby([config.redisNamespace, 'metric:migrate:path:count:h'].join(':'), path, 1);
80
- });
81
- }
82
- }
83
- let [cachedContent] = await multiExecAsync(client, multi => {
84
- multi.get(cacheKey);
85
- multi.expire(cacheKey, config.expireSeconds);
86
- multi.hincrby([config.redisNamespace, 'metric:get:path:count:h'].join(':'), path, 1);
87
- });
88
- if (cachedContent) {
89
- logger.debug('hit', {url, sha, cacheKey});
90
- const parsedContent = JSON.parse(cachedContent);
91
- const formattedContent = JSON.stringify(parsedContent);
92
- if (cachedContent !== formattedContent) {
93
- logger.debug('reformat', cacheKey, cachedContent);
94
- await multiExecAsync(client, multi => {
95
- multi.set(cacheKey, formattedContent);
96
- });
97
- }
98
- if (lodash.includes(['OK', 'ZERO_RESULTS'], parsedContent.status)) {
99
- logger.warn('hit', {url, sha, cacheKey, parsedContent});
100
- ctx.set('Content-Type', 'application/json');
101
- ctx.body = JSON.stringify(parsedContent, null, 2) + '\n';
102
- return;
103
- }
104
- }
105
- const urlQuery = url + '?' + Object.keys(authQuery)
106
- .map(key => [key, encodeURIComponent(authQuery[key])].join('='))
107
- .join('&');
108
- const res = await fetch(urlQuery);
109
- if (res.status !== 200) {
110
- logger.debug('statusCode', url, res.status, res.statusText, query);
111
- ctx.statusCode = res.status;
112
- ctx.body = res.statusText + '\n';
113
- return;
114
- }
115
- const fetchedContent = await res.json();
116
- const formattedContent = JSON.stringify(fetchedContent, null, 2) + '\n';
117
- ctx.set('Content-Type', 'application/json');
118
- ctx.body = formattedContent;
119
- if (!lodash.includes(['OK', 'ZERO_RESULTS'], fetchedContent.status)) {
120
- logger.debug('status', fetchedContent.status, url);
121
- } else {
122
- const expireSeconds = lodash.includes(['ZERO_RESULTS'], fetchedContent.status)?
123
- config.shortExpireSeconds:
124
- config.expireSeconds;
125
- logger.debug('expireSeconds', expireSeconds, fetchedContent.status, url);
126
- await multiExecAsync(client, multi => {
127
- multi.setex(cacheKey, expireSeconds, JSON.stringify(fetchedContent));
128
- multi.hincrby([config.redisNamespace, 'metric:set:path:count:h'].join(':'), path, 1);
129
- });
130
- }
131
- });
132
- }
package/lib/spec.js DELETED
@@ -1,50 +0,0 @@
1
- module.exports = {
2
- description: 'Redis-based caching proxy for Google Maps API queries.',
3
- required: {
4
- redisHost: {
5
- description: 'the Redis host',
6
- default: 'localhost'
7
- },
8
- redisPort: {
9
- description: 'the Redis port',
10
- default: 6379
11
- },
12
- redisPassword: {
13
- description: 'the Redis password',
14
- required: false
15
- },
16
- redisNamespace: {
17
- description: 'the Redis namespace',
18
- default: 'cache-geo'
19
- },
20
- expireSeconds: {
21
- description: 'the TTL for the cached content',
22
- default: 21*24*3600
23
- },
24
- shortExpireSeconds: {
25
- description: 'the TTL for the cached content',
26
- default: 3*24*3600
27
- },
28
- httpPort: {
29
- description: 'the HTTP port',
30
- default: 8851
31
- },
32
- apiKey: {
33
- description: 'our Google Maps API key',
34
- required: false
35
- },
36
- loggerLevel: {
37
- description: 'the logging level',
38
- default: 'info',
39
- example: 'debug'
40
- }
41
- },
42
- test: {
43
- loggerLevel: 'debug',
44
- debug: true
45
- },
46
- development: {
47
- loggerLevel: 'debug',
48
- debug: true
49
- }
50
- }
package/test/curl.sh DELETED
@@ -1,9 +0,0 @@
1
-
2
- set -u -x
3
-
4
- curl 'http://localhost:8851/maps/api/geocode/json' -G --data-urlencode 'address=20 Falstaff Close, Eynsham OX29 4QA'
5
- curl 'http://localhost:8851/maps/api/geocode/json' -G --data-urlencode 'address=14 Grays Inn Road, Chancery Lane, London'
6
- sleep 1
7
- redis-cli keys cache-geo-cache:*
8
- redis-cli hkeys cache-geo-cache:url:h
9
- redis-cli hgetall cache-geo-cache:url:h