-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DRAFT] Add Node.js feature #67
Draft
Taliik
wants to merge
11
commits into
main
Choose a base branch
from
node-js-feature
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
6483a4f
added node-js sample app
249b951
fixed port number
8c84514
simplified request body parsing, fixed unit test errors with method P…
ff50185
image upload rework
b69a38a
image upload fixed
5682ae1
image upload fixed
01aaa0b
fixed all text methods to work with the test.py
ca6cd09
integration test works now. when uploading an image, the test has to …
6eede71
fixed immediate get image request from integration test
0aefd17
added node.js support
4bec4b9
added newline in gitignore
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,4 @@ php/composer.lock | |
java/target/* | ||
java/java.iml | ||
java/.idea | ||
node-js/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,317 @@ | ||
//requirements | ||
var uuid = require('uuid/v1'); | ||
var sha1 = require('sha1'); | ||
var crate = require('node-crate'); | ||
var app = require('express')(); | ||
var http = require('http'); | ||
var bodyParser = require('body-parser'); | ||
|
||
//configurables | ||
var crate_host = 'localhost'; | ||
var crate_port_number = 4200; | ||
var crate_port = ''+crate_port_number; | ||
var service_port = 8080; | ||
|
||
//bootstrap | ||
crate.connect(crate_host, crate_port); | ||
app.all('/*', function(req, res, next) { | ||
res.setHeader("Access-Control-Allow-Origin", "*"); | ||
res.setHeader("Access-Control-Allow-Credentials", "true"); | ||
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, UPDATE, DELETE, OPTION"); | ||
res.setHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); | ||
next(); | ||
}) | ||
app.use(bodyParser.json({limit: '5mb'})); // for parsing application/json | ||
app.use(bodyParser.raw({limit: '5mb'})); | ||
app.use(bodyParser.urlencoded({limit: '5mb', extended: true })); // for parsing application/x-www-form-urlencoded | ||
|
||
//rest-service-functions | ||
//############POST-RESOURCE########################## | ||
|
||
//POST /posts - Create a new post. | ||
app.post('/posts', function(req, res){ | ||
res.setHeader('Content-Type', 'application/json'); | ||
|
||
//parsed data is found in req.body | ||
|
||
//check for required data | ||
if(!req.body.text) { | ||
res.status(400).json({ | ||
error: 'Argument "text" is required', | ||
status: 400 | ||
}); | ||
return; | ||
} | ||
if(!req.body.user.location) { | ||
res.status(400).json({ | ||
error: 'Argument "location" is required', | ||
status: 400 | ||
}) | ||
return; | ||
} | ||
|
||
var tablename = 'guestbook.posts'; | ||
var id = uuid(); | ||
//insert | ||
crate.insert( | ||
tablename, | ||
{ | ||
"id": id, | ||
"user": req.body.user, | ||
"text": req.body.text, | ||
"created": new Date().getTime(), | ||
"image_ref": req.body.image_ref, | ||
"like_count": 0 | ||
}).then(() => { | ||
//refresh table to make sure new record is immediately available | ||
refreshTable().then(() => { | ||
//fetch new record | ||
getPost(id).then((response) => { | ||
res.status(201).json(response.json); | ||
}) | ||
}) | ||
}); | ||
}); | ||
|
||
//GET /posts - Retrieve a list of all posts. | ||
app.get('/posts', function(req, res) { | ||
res.setHeader('Content-Type', 'application/json'); | ||
|
||
getPosts().then((response) => { | ||
res.status(200).json(response.json); | ||
}); | ||
}); | ||
|
||
//GET /post/:id - Retrieves the post with the corresponding id. | ||
app.get('/post/:id', function(req, res) { | ||
res.setHeader('Content-Type', 'application/json'); | ||
|
||
var id = req.params.id; | ||
|
||
getPost(id).then((response) => { | ||
if(response.rowcount > 0){ | ||
res.status(200).json(response.json[0]); | ||
}else { | ||
res.status(404).json({ | ||
error: 'Post with id="'+id+'" not found', | ||
status: 404 | ||
}); | ||
} | ||
}); | ||
}); | ||
|
||
//PUT /post/{id} - Updates text property of given id. | ||
app.put('/post/:id', function(req, res) { | ||
res.setHeader('Content-Type', 'application/json'); | ||
|
||
var id = req.params.id; | ||
var text = req.body.text; | ||
|
||
if(!text) { | ||
res.status(400).json({ | ||
error: 'Argument "text" is required', | ||
status: 400 | ||
}) | ||
return; | ||
} | ||
|
||
updatePost(id, text).then((_) => { | ||
refreshTable().then((_) => { | ||
getPost(id).then((response) => { | ||
res.status(200).json(response.json[0]); | ||
}) | ||
}) | ||
}).catch(() => { | ||
res.status(404).end(); | ||
}) | ||
}); | ||
|
||
|
||
|
||
//### `PUT /post/<id>/like` Increments the like count for a given post by one. | ||
app.put('/post/:id/like', function(req, res){ | ||
res.setHeader('Content-Type', 'application/json'); | ||
|
||
var id = req.params.id | ||
getPost(id).then((response) => { | ||
var newLikes = response.json[0].like_count + 1; | ||
likePost(id, newLikes).then(() => { | ||
response.json[0].like_count += 1; | ||
refreshTable().then(()=>{ | ||
res.status(200).json(response.json[0]); | ||
}) | ||
}) | ||
}).catch(() => { | ||
res.status(404).json({ | ||
error: 'Post with id="'+id+'" not found', | ||
status: 404 | ||
}) | ||
}) | ||
}); | ||
|
||
//### `DELETE /post/<id>` Delete a post with given `id`. | ||
app.delete('/post/:id', function(req, res){ | ||
|
||
var id = req.params.id; | ||
|
||
deletePost(id).then((response) => { | ||
if(response.rowcount>0) { | ||
res.status(204).json(response.json); | ||
} | ||
else { | ||
res.status(404).json({ | ||
error: 'Post with id="'+id+'" not found', | ||
status: 404 | ||
}); | ||
} | ||
}) | ||
}); | ||
|
||
//### `POST /search` Issue a search request to fetch a list of posts whose ``text`` matches a given query string. | ||
app.post('/search', function(req, res){ | ||
res.setHeader('Content-Type', 'application/json'); | ||
|
||
var searchText = req.body.query_string; | ||
|
||
if(!searchText) { | ||
res.status(400).json({ | ||
error: 'Argument "query_string" is required', | ||
status: 400 | ||
}) | ||
return; | ||
} | ||
|
||
var query = ("SELECT p.*, p._score AS _score, c.name AS country, c.geometry AS area FROM guestbook.posts AS p, guestbook.countries AS c WHERE within(p.user['location'], c.geometry) AND match(text, ?) ORDER BY _score DESC"); | ||
|
||
crate.execute(query, [searchText]).then((response) => { | ||
res.status(200).json(response.json); | ||
}) | ||
}); | ||
|
||
//############IMAGE-RESOURCE########################## | ||
app.post('/images', function(req, res){ | ||
res.setHeader('Content-Type', 'application/json'); | ||
|
||
if(!req.body.blob){ | ||
res.status(400).json({ | ||
error: 'Argument "blob" is required', | ||
status: 400 | ||
}); | ||
return; | ||
} | ||
|
||
var b64String = req.body.blob; | ||
var buf = Buffer.from(b64String, 'base64'); | ||
|
||
var encrypted = sha1(buf); | ||
|
||
var urlValue = '/image/'+encrypted; | ||
|
||
var result = {}; | ||
result['url'] = urlValue; | ||
result['digest'] = encrypted; | ||
|
||
crate.insertBlob('guestbook_images', buf).then(() => { | ||
res.status(201).json(result); | ||
}).catch(() => { | ||
res.status(409).json(result); | ||
}); | ||
}); | ||
|
||
app.get('/images', function(req, res) { | ||
res.setHeader('Content-Type', 'application/json'); | ||
|
||
getImages().then((response) => { | ||
res.status(200).json(response.json); | ||
}) | ||
}) | ||
|
||
app.get('/image/:digest', function(req, res) { | ||
console.log('GET /images was called with digest: '+req.params.digest); | ||
crate.getBlob('guestbook_images', req.params.digest).then((data) => { | ||
console.log(req.params.digest + ' has a length of ' + data.length); | ||
if(data.length>0) { | ||
res.setHeader('Content-Type', 'image/gif'); | ||
res.setHeader('Content-Length', ''+data.length+''); | ||
res.status(200).end(data); | ||
} | ||
else { | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.status(404).json({ | ||
error: 'Image with digest="'+req.params.digest+'" not found', | ||
status: 404 | ||
}); | ||
} | ||
}) | ||
}); | ||
|
||
//### `DELETE /image/<digest>` Delete an image with given `digest`. | ||
app.delete('/image/:digest', function(req, res){ | ||
var digest = req.params.digest; | ||
|
||
var options = { | ||
host: crate_host, | ||
port: crate_port_number, | ||
path: '/_blobs/guestbook_images/'+digest, | ||
method: 'DELETE' | ||
}; | ||
http.request(options, (response) => { | ||
res.status(response.statusCode); | ||
if(response.statusCode!=204) { | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.json({ | ||
error: 'Image with digest="'+digest+'" not found', | ||
status: 404 | ||
}); | ||
} | ||
else { | ||
res.end(); | ||
} | ||
}).end(); | ||
}); | ||
|
||
//launch | ||
var server = app.listen(service_port, function() { | ||
var host = server.address().address; | ||
|
||
console.log("app listening at http://%s:%s", host, service_port); | ||
}) | ||
|
||
//helper | ||
function getPosts() { | ||
return crate.execute("SELECT p.*, c.name as country, c.geometry as area FROM guestbook.posts AS p, guestbook.countries AS c WHERE within(p.user['location'], c.geometry) ORDER BY p.created DESC"); | ||
} | ||
|
||
function getPost(id) { | ||
var query = ("SELECT p.*, c.name as country, c.geometry as area FROM guestbook.posts AS p, guestbook.countries AS c WHERE within(p.user['location'], c.geometry) AND p.id=?"); | ||
return crate.execute(query, [id]); | ||
} | ||
|
||
function updatePost(id, newText) { | ||
var where = "id='"+id+"'"; | ||
return crate.update('guestbook.posts', { text: newText }, where); | ||
} | ||
|
||
function likePost(id, newLikes) { | ||
var where = "id='"+id+"'"; | ||
return crate.update('guestbook.posts', { like_count: newLikes }, where); | ||
} | ||
|
||
function deletePost(id) { | ||
var query = ("DELETE FROM guestbook.posts WHERE id=?"); | ||
return crate.execute(query, [id]); | ||
} | ||
|
||
function getImages(){ | ||
return crate.execute("SELECT digest, last_modified " + "FROM Blob.guestbook_images " + "ORDER BY 2 DESC"); | ||
} | ||
|
||
function getImage(digest) { | ||
var query = "SELECT digest FROM Blob.guestbook_images WHERE digest='?'"; | ||
return crate.execute(query, [digest]); | ||
} | ||
|
||
function refreshTable() { | ||
return crate.execute("REFRESH TABLE guestbook.posts"); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "crate-sample-apps", | ||
"version": "1.0.0", | ||
"description": "node.js support for crate-sample-apps", | ||
"main": "controller.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [ | ||
"crate", | ||
"sample" | ||
], | ||
"author": "Alex, Fabian", | ||
"license": "Apache-2.0", | ||
"dependencies": { | ||
"express": "^4.15.3", | ||
"node-crate": "^2.0.1", | ||
"sha1": "^1.1.1", | ||
"uuid": "^3.1.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like
node-crate
is unmaintained 1. What about switching to the canonicalnode-postgres
2 today?Footnotes
https://www.npmjs.com/package/node-crate ↩
https://www.npmjs.com/package/pg ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Contrary to my previous assessment, it looks like
node-crate
is well alive and maintained 1.Footnotes
https://github.com/megastef/node-crate ↩