Skip to content

Commit a6a474d

Browse files
zackbloomsevki
authored andcommitted
initial react workers
1 parent 1fc54fa commit a6a474d

File tree

11 files changed

+4863
-40
lines changed

11 files changed

+4863
-40
lines changed

LICENSE

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2018, Cloudflare, Inc.
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
7+
* Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
10+
* Redistributions in binary form must reproduce the above copyright notice,
11+
this list of conditions and the following disclaimer in the documentation
12+
and/or other materials provided with the distribution.
13+
14+
* Neither the name of the copyright holder nor the names of its
15+
contributors may be used to endorse or promote products derived from
16+
this software without specific prior written permission.
17+
18+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
1-
Workers Webpack Example
2-
====
3-
4-
[Cloudflare Workers](http://developers.cloudflare.com/workers/) allow you to write JavaScript which runs on all of Cloudflare's
5-
160+ global data centers. This repo is an example of how to combine multiple files and dependencies to create a Worker using
6-
the [Webpack](https://webpack.js.org/) build tool.
1+
![Cloudflare GraphQL Gateway](https://www.cloudflare.com/img/products/cloudflare-workers/workers-api-responses.png)
72

8-
The actual Worker replaces the content of your site with a Worker which returns the current time in the timezone of the caller's
9-
choice.
3+
Workers GraphQL Gateway Example
4+
====
5+
Building a GraphQL Gateway allows you to make a single query, have GraphQL and take care of breaking it down to multiple rest calls. This example makes use of [dataloader](https://github.com/facebook/dataloader) to batch and cache the queries so a query with the same key will not be requested twice.
106

117
### Dependencies
12-
13-
- [npm](https://www.npmjs.com/get-npm)
14-
- [jq](https://stedolan.github.io/jq/) (for the preview script)
15-
- [cURL](https://curl.haxx.se/) (for the preview script)
8+
- [npm](https://www.npmjs.com/get-npm) or [yarn](https://yarnpkg.com/en/docs/install#debian-stable)
169

1710
### Instructions
1811

@@ -22,3 +15,9 @@ choice.
2215
To open the Workers preview with the built Worker:
2316

2417
- `npm run preview`
18+
19+
### About
20+
[Cloudflare Workers](http://developers.cloudflare.com/workers/) allow you to write JavaScript which runs on all of Cloudflare's
21+
150+ global data centers.
22+
23+
[GraphQL](https://graphql.org) provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@
44
"description": "",
55
"scripts": {
66
"build": "webpack -p --progress --colors",
7-
"preview": "./scripts/open-preview < dist/worker.js"
7+
"preview": "node ./scripts/open-preview.js"
88
},
99
"main": "dist/worker.js",
1010
"author": "",
1111
"license": "MIT",
12+
"devDependencies": {
13+
"node-fetch": "^2.2.0",
14+
"opn": "^5.3.0"
15+
},
1216
"dependencies": {
13-
"moment-timezone": "^0.5.17",
17+
"dataloader": "^1.4.0",
18+
"graphql": "^0.13.2",
19+
"webpack": "^4.10.2",
1420
"webpack-cli": "^2.1.4"
1521
}
1622
}

scripts/open-preview

Lines changed: 0 additions & 8 deletions
This file was deleted.

scripts/open-preview.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const fs = require("fs");
2+
const util = require("util");
3+
const fetch = require("node-fetch");
4+
const readFile = util.promisify(fs.readFile);
5+
const opn = require("opn");
6+
7+
async function newWorker(script) {
8+
let resp = await fetch("https://cloudflareworkers.com/script", {
9+
method: "POST",
10+
headers: {
11+
"cache-control": "no-cache",
12+
"content-type": "text/javascript"
13+
},
14+
body: script
15+
});
16+
17+
let data = await resp.json();
18+
19+
return data.id;
20+
}
21+
22+
readFile("dist/worker.js", "utf8").then(data => {
23+
newWorker(data).then(id =>
24+
opn(
25+
"https://cloudflareworkers.com/#" + id + ":https://cloudflaregraphql.com",
26+
{ app: "chromium" }
27+
)
28+
);
29+
});

src/all.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
import { handleRequest } from './time'
1+
/**
2+
* Copyright (c) 2018, Cloudflare, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
import { handleRequest } from "./router";
210

3-
addEventListener('fetch', event => {
4-
event.respondWith(handleRequest(event.request))
5-
})
11+
addEventListener("fetch", event => {
12+
event.respondWith(handleRequest(event));
13+
});

src/graphql.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* Copyright (c) 2018, Cloudflare, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
import { graphql, buildSchema } from "graphql";
10+
import DataLoader from "dataloader";
11+
12+
function binary_to_string(array) {
13+
var result = "";
14+
for (var i = 0; i < array.length; ++i) {
15+
result += String.fromCharCode(array[i]);
16+
}
17+
return result;
18+
}
19+
20+
async function decodequery(request) {
21+
const reader = request.body.getReader();
22+
let query = "";
23+
while (true) {
24+
let { done, value } = await reader.read();
25+
if (done) {
26+
break;
27+
}
28+
query = query + binary_to_string(value);
29+
}
30+
let gql = JSON.parse(query);
31+
return gql;
32+
}
33+
34+
var schema = buildSchema(`
35+
"DNS record type."
36+
enum RecordType {
37+
A
38+
AAAA
39+
MX
40+
CNAME
41+
DNSKEY
42+
DS
43+
NAPTR
44+
NS
45+
PTR
46+
SPF
47+
SRV
48+
SSHFP
49+
TLSA
50+
TXT
51+
}
52+
53+
"DNS query response"
54+
type Answer {
55+
"The record owner."
56+
name: String
57+
"""The type of DNS record.
58+
These are defined here:
59+
https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
60+
"""
61+
type: Int
62+
"""The number of seconds the answer can be stored
63+
in cache before it is considered stale."""
64+
TTL: Int
65+
"""The value of the DNS record for the given name and type.
66+
The data will be in text for standardized record types and in hex for unknown types."""
67+
data: String
68+
}
69+
"A DNS query to resolve a DNS record of a given type."
70+
type Query {
71+
resolve(name: String!, type: RecordType!): [Answer]
72+
}
73+
`);
74+
75+
async function resolve(event, x) {
76+
let req = new Request(
77+
"https://cloudflare-dns.com/dns-query?name=" + x.name + "&type=" + x.type,
78+
{
79+
headers: {
80+
Accept: "application/dns-json"
81+
}
82+
}
83+
);
84+
85+
let cache = await caches.open("dns");
86+
let resp = await cache.match(req);
87+
88+
if (!resp) {
89+
resp = await fetch(req);
90+
event.waitUntil(cache.put(req, resp.clone()));
91+
}
92+
let ans = await resp.json();
93+
return ans.Answer;
94+
}
95+
96+
async function batchResolver(event, keys) {
97+
return keys.map(id => resolve(event, id));
98+
}
99+
100+
self.cache = new Map();
101+
102+
class Root {
103+
constructor(event) {
104+
this.resolvers = new DataLoader(keys => batchResolver(event, keys), {
105+
cacheKeyFn: q => {
106+
q.type + q.name;
107+
},
108+
cacheMap: self.cache
109+
});
110+
}
111+
async resolve(x) {
112+
return this.resolvers.load(x);
113+
}
114+
}
115+
116+
export default async function handleGraphQLRequest(event) {
117+
let gql = await decodequery(event.request);
118+
let response = await graphql(schema, gql.query, new Root(event));
119+
return new Response(JSON.stringify(response));
120+
}

src/router.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) 2018, Cloudflare, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
import handleGraphQLRequest from "./graphql";
10+
11+
export async function handleRequest(event) {
12+
let u = new URL(event.request.url);
13+
switch (u.pathname) {
14+
case "/graphql":
15+
return await handleGraphQLRequest(event);
16+
case "/":
17+
return await fetch("https://storage.googleapis.com/cfgraphql/index.html");
18+
case "/graphiql/cfgql.css":
19+
return await fetch("https://storage.googleapis.com/cfgraphql/cfgql.css");
20+
case "/graphiql/cfgql.js":
21+
return await fetch("https://storage.googleapis.com/cfgraphql/cfgql.js");
22+
default:
23+
return new Response(JSON.stringify("OK"));
24+
}
25+
}

src/time.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

webpack.config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const path = require("path");
12
module.exports = {
23
entry: "./src/all.js",
34
mode: "development",
@@ -11,5 +12,19 @@ module.exports = {
1112
path: __dirname + "/dist",
1213
publicPath: "dist",
1314
filename: "worker.js"
15+
},
16+
module: {
17+
rules: [
18+
{
19+
test: /\.mjs$/,
20+
type: "javascript/auto",
21+
use: []
22+
},
23+
{
24+
test: /\.js$/,
25+
type: "javascript/auto",
26+
use: []
27+
}
28+
]
1429
}
1530
};

0 commit comments

Comments
 (0)