diff --git a/Makefile b/Makefile index 3952bb3f6..586f735ab 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,11 @@ build: wasm-pack --version && \ - wasm-pack build --target web --out-dir pkg-web/ \ No newline at end of file + wasm-pack build --target web --out-dir pkg-web/ + +container-build: + wasm-pack --version && \ + wasm-pack build --target nodejs --out-dir pkg-node/ && \ + docker build -f docker/Dockerfile . -t partiql-team/partiql-playground + +container-run: + docker run -d -p 8000:8000 partiql-team/partiql-playground \ No newline at end of file diff --git a/README.md b/README.md index 96c3097e1..f0c4c4d6f 100644 --- a/README.md +++ b/README.md @@ -45,14 +45,44 @@ npm i npm run serve ``` +## Docker Build +```commandline +make container-build +make container-run +``` + +### Restful API +```commandline +# Example for parsing `SELECT * FROM {'a': 1}` statement +curl -H 'Content-Type: application/json; charset=UTF-8' \ + -H "Accept: application/json" \ + --data '{"query": "SELECT * FROM {'a': 1}"}' \ + -X POST http://localhost:8000/parse + +"{\"text\":\"SELECT * FROM {a: 1}\",\"offsets\":{\"line_starts\":[0]},\"ast\":{\"Query\":{\"id\":9,\"node\":{\"set\":{\"id\":8,\"node\":{\"Select\":{\"id\":7,\"node\":{\"project\":{\"id\":1,\"node\":{\"kind\":\"ProjectStar\",\"setq\":\"All\"}},\"from\":{\"id\":6,\"node\":{\"source\":{\"FromLet\":{\"id\":5,\"node\":{\"expr\":{\"Struct\":{\"id\":4,\"node\":{\"fields\":[{\"first\":{\"VarRef\":{\"id\":2,\"node\":{\"name\":{\"value\":\"a\",\"case\":\"CaseInsensitive\"},\"qualifier\":\"Unqualified\"}}},\"second\":{\"Lit\":{\"id\":3,\"node\":{\"Int64Lit\":1}}}}]}}},\"kind\":\"Scan\",\"as_alias\":null,\"at_alias\":null,\"by_alias\":null}}}}},\"from_let\":null,\"where_clause\":null,\"group_by\":null,\"having\":null}}}},\"order_by\":null,\"limit\":null,\"offset\":null}}},\"locations\":{\"1\":{\"start\":0,\"end\":8},\"2\":{\"start\":15,\"end\":16},\"3\":{\"start\":18,\"end\":19},\"4\":{\"start\":14,\"end\":20},\"5\":{\"start\":14,\"end\":20},\"6\":{\"start\":9,\"end\":20},\"7\":{\"start\":0,\"end\":20},\"8\":{\"start\":0,\"end\":20},\"9\":{\"start\":0,\"end\":20}}}"% + +# Example for explaining (logical planning) `SELECT * FROM {'a': 1}` statement +curl -H 'Content-Type: application/json; charset=UTF-8' \ + -H "Accept: application/json" \ + --data '{"query": "SELECT * FROM {'a': 1}"}' \ + -X POST http://localhost:8000/explain + +"\"LogicalPlan { nodes: [ProjectAll, Scan(Scan { expr: TupleExpr(TupleExpr { attrs: [VarRef(CaseInsensitive(\\\"a\\\"))], values: [Lit(1)] }), as_key: \\\"_1\\\", at_key: None }), Sink], edges: [(OpId(2), OpId(1), 0), (OpId(1), OpId(3), 0)] }\""% + +# Example for evaluating `SELECT * FROM env` statement +curl -H 'Content-Type: application/json; charset=UTF-8' \ + -H "Accept: application/json" \ + --data '{"query": "SELECT * FROM env", "env": "{'\''env'\'' : <<{'\''a'\'': 1,'\''b'\'': 2 }>>}"}' \ + -X POST http://localhost:8000/eval + +"{\"Ok\":{\"Bag\":[{\"Tuple\":{\"attrs\":[\"a\",\"b\"],\"vals\":[{\"Integer\":1},{\"Integer\":2}]}}]}}"% +``` -## Legacy Asset -| Branch Name | Asset | -|-------------|-----------------------------------------------------------------------------------------------------| -| Docker | A version of PartiQL Playground implementation. Additional features includes docker build Rest APIs | ## Dependencies | Package | License | |------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| | [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) | [Apache License Version 2.0](https://github.com/rustwasm/wasm-bindgen/blob/main/LICENSE-APACHE) | -| [wasm-pack](https://github.com/rustwasm/wasm-pack) | [Apache License Version 2.0](https://github.com/rustwasm/wasm-pack/blob/master/LICENSE-APACHE) | \ No newline at end of file +| [wasm-pack](https://github.com/rustwasm/wasm-pack) | [Apache License Version 2.0](https://github.com/rustwasm/wasm-pack/blob/master/LICENSE-APACHE) | +| [node](https://nodejs.org/en/) | [MIT License](https://github.com/nodejs/node/blob/main/LICENSE) | +| [body-parser](https://github.com/expressjs/body-parser) | [MIT License](https://github.com/expressjs/body-parser/blob/master/LICENSE) | \ No newline at end of file diff --git a/docker/.dockerignore b/docker/.dockerignore new file mode 100644 index 000000000..5171c5408 --- /dev/null +++ b/docker/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..9b8476b02 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,20 @@ +FROM node:18 +# Create app directory +WORKDIR /usr/src/app + +# Install app dependencies +# A wildcard is used to ensure both package.json AND package-lock.json are copied +# where available (npm@5+) +COPY ./docker/package*.json ./ +COPY ./pkg-node ./ + +RUN npm install +# If you are building your code for production +RUN npm ci --only=production + +# Bundle app source +COPY . . + +EXPOSE 8080 + +CMD [ "node", "docker/server.ts" ] \ No newline at end of file diff --git a/docker/package.json b/docker/package.json new file mode 100644 index 000000000..c7375a85e --- /dev/null +++ b/docker/package.json @@ -0,0 +1,27 @@ +{ + "name": "partiql-playground", + "version": "0.1.0", + "description": "PartiQL Playground", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/partiql-lang-rust/partiql-playground.git" + }, + "keywords": [ + "PartiQL", + "Playground" + ], + "author": "partiql-team@amazon.com", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/partiql-lang-rust/partiql-playground/issues" + }, + "homepage": "https://github.com/partiql-lang-rust/partiql-playground#readme", + "dependencies": { + "body-parser": "^1.20.0", + "express": "^4.18.1" + } +} \ No newline at end of file diff --git a/docker/server.ts b/docker/server.ts new file mode 100644 index 000000000..788f211b7 --- /dev/null +++ b/docker/server.ts @@ -0,0 +1,40 @@ +const express = require('express'); +const bodyParser = require("body-parser"); +const router = express.Router(); +const partiql = require('../pkg-node/partiql_playground'); + +const app = express(); +const port = 8000; + + +app.use(bodyParser.urlencoded({ extended: false })); +app.use(bodyParser.json()); + +router.post('/parse', (req, res) => { + res.status(200) + .json(JSON.stringify(JSON + .parse(partiql.parse_as_json(req.body.query) + ) + )); +}); + +router.post('/explain', (req, res) => { + res.status(200) + .json(JSON.stringify(JSON + .parse(partiql.explain_as_json(req.body.query) + ) + )); +}); + +router.post('/eval', (req, res) => { + res.status(200) + .json(JSON.stringify(JSON + .parse(partiql.eval_as_json(req.body.query, req.body.env)) + )); +}); + +app.use("/", router); + +app.listen(port, () => { + console.log(`Now listening on port ${port}`); +}); \ No newline at end of file