Restify JsonClient URL x Path - node.js

When I create a JsonClient in node I do the following:
var client = restify.createJsonClient({
url: 'https://www.domain.com:4321/api'
});
Once I've done that, I make calls like so:
client.post('/service/path', { });
Which seems right. I expect that the path called would be something like https://www.domain.com:4321/api/service/path. However, what is happening is that the client is throwing away the /api base path and calling https://www.domain.com:4321/service/path.
I don't get it - I'm inserting the client URL into a config file, so that I can change hosts without any hassle; Now that I need a base path, I need to change the code as well as the config.

If you put a wrapper around the restify JsonClient stuff you could do it with minimal code change and the config would, I think, work the way you want it.
Create a library file myClient.js
'use strict';
var restify = require('restify');
var jsonClient = null;
module.exports = {
createJsonClient: function(opts){
var opts = opts || {};
var url = opts.url;
var parts = url.split('/');
var main_url = parts[0] + '//' + parts[2];
var basePath = parts[3] ? parts[3] : '';
jsonClient = restify.createJsonClient({url: main_url});
return {
get: function(path, cb){
var adjusted_path = '/' + basePath + path;
jsonClient.get(adjusted_path, function(err2, req2, res2, obj2){
return cb(err2, req2, res2, obj2);
});
}
}
}
}
Then use it like this.
var myClientWrapper = require('./lib/myClient');
var client = myClientWrapper.createJsonClient({url: 'http://localhost:8000/api'});
client.get('/service/path/one', function(err, req, res, obj){
if(err){
console.log(err.message);
return;
}
console.log(res.body);
});
It could use some more error checking and the url parsing is a little brittle, but it does work. I tried it out. Of course, I only wrapped the get function but you can see how it would work for the others.

Related

Node js App to display folder files

I am trying to display file list from folders .
my folder structure is like below
Invoices
1. error
2. processed
3. unprocessed
I have created node api for same which i am calling on my html page. code for the same is as below
const fs = require('fs');
var express = require('express');
var cors = require('cors');
var app = express();
app.use(cors());
app.use(express.static(__dirname));
var flength;
var filename;
var currentFile;
var items = [];
var dir1 = 'Invoices';
var filepath = [];
var readFolder = function(dir1) {
var countt = function(filename) {
var currentFile = dir1 + '/' + filename;
fs.readdir(currentFile, (err, files) => {
flength = files.length;
var fileArrayList = [];
for (var f in files) {
var record = {
filename: files[f],
filepath: dir1 + '/' + filename + '/' + files[f]
}
fileArrayList.push(record);
}
items.push({
'file': filename,
'count': flength,
'files': fileArrayList
});
});
}
var ReadFirst = function(dir1) {
fs.readdir(dir1, (err, files) => {
for (var i in files) {
var filename = files[i];
var currentFile = dir1 + '/' + filename;
var stats = fs.statSync(currentFile);
if (stats.isDirectory()) {
countt(filename);
}
}
});
}
ReadFirst(dir1);
}
setTimeout(function(str1, str2) {
readFolder(dir1);
}, 1000);
app.get('/FileCount', function(req, res) {
res.send(items);
});
app.listen(4000);
console.log('Listening on port 4000');
When i add or delete files from any folder then its not reflecting on my html page.need help for this.
thank you.
This is happening because of the way you've implemented this.
Your client (the HTML page) requests the server (NodeJS) API for
some data. In this case, it is the list of files in a folder. The server sends the response based on the state of files at the time (plus ∆).
You display those results in the HTML page. Now, there is no live link between your HTML page and your backend server. This means any changes that happen after this point won't be automatically reflected in the page.
You can do two things here:
Call your API repeatedly after an interval of few seconds. (If you're using AngularJS, look into setTimeout function.
Use sockets for having a real-time link. One good documentation here:
https://loopback.io/doc/en/lb2/Realtime-socket-io.html
This is more of a design issue and your NodeJS API looks fine.

Loopback : Rename file from before remote method using context

I want to rename file from before remote hook using context.
container.beforeRemote('upload', function (context, res, next) {
/////rename file
}
Can anyone tell me how can i access files from this?
I don't know if it's possible to do it before because we haven't unpacked the multipart form yet.
The afterRemote hooks contains enough information to rename the file if you really need to. Here's an example built on top of loopback's default storage example
app.start = function() {
// Adding an operation hook which renames the recently uploaded file
var container = app.dataSources.storage.models.container;
container.afterRemote('upload', (context, res, next) => {
// The file object is stored in the res param
let file = res.result.files.file[0];
// Get the filepath of our datasource, in this case `storage`.
let root = container.dataSource.settings.root;
// Get the full path of the file we just uploaded
// root/containerName/filename.ext
let filePath = path.resolve(root, file.container, file.name);
// aand rename
fs.rename(filePath, path.resolve(root, file.container, 'newFile.txt'), () => console.log('renamed!'));
});
return app.listen(function() {
app.emit('started');
var baseUrl = app.get('url').replace(/\/$/, '');
console.log('Web server listening at: %s', baseUrl);
if (app.get('loopback-component-explorer')) {
var explorerPath = app.get('loopback-component-explorer').mountPath;
console.log('Browse your REST API at %s%s', baseUrl, explorerPath);
}
});
};
Here is a boot script which will rename the file using a UUID (You need to install UUID package). You can apply other logic such as timestamp etc for renaming the file.
var uuid = require('uuid-v4');
module.exports = function(app) {
var uuid = require('uuid-v4');
module.exports = function(app) {
app.dataSources.storage.connector.getFilename = function(origFilename, req, res) {
var origFilename = origFilename.name;
var parts = origFilename.split('.'),
extension = parts[parts.length - 1];
return uuid() + '.' + extension;
}
}
}

Node appendFile not appending data chunk to file on filesystem

I have a program that is trying to get the values from the request using curl and store them in a file and serve the stored content back. The decision to store or append the contents in file are based on a query parameter appendFlag
Now when i run this program what i am getting in console is "true" and "appending" This suggests that it indeed reads the flag goes to the if part but somehow the appendFile function is not working.
var http = require('http');
var url = require('url');
var querystring = require('querystring');
var fs = require('fs');
http.createServer(function(request,response){
var str = request.url.split('?')[1];
var query = querystring.parse(str);
var writeStream = fs.createWriteStream(query['fileName']);
console.log("query - ");
console.log(query["appendFlag"]);
request.on('data',function(chunk){
if(query["appendFlag"]=="true"){
console.log("appending");
fs.appendFile(query['fileName'],chunk.toString(),function(err){
if(err) throw err;
});
}else{
var bufferGood = writeStream.write(chunk);
if(!bufferGood) request.pause();
}
});
request.on('end',function(){
response.writeHead(200);
response.write("\n Content with this url is - \n");
var readStream = fs.createReadStream(query['fileName'],{bufferSize:64*1024});
readStream.on('data',function(chunk){
response.write(chunk.toString());
});
readStream.on('end',function(){
response.write("\n");
response.end();
});
});
writeStream.on('drain',function(){
request.resume();
});
}).listen(8080);
Then after reading an answer from SO( How to create appending writeStream in Node.js ) i tried -
// Program to extract url from the request and writing in that particular file
var http = require('http');
var url = require('url');
var querystring = require('querystring');
var fs = require('fs');
http.createServer(function(request,response){
var str = request.url.split('?')[1];
var query = querystring.parse(str);
var writeStream = fs.createWriteStream(query['fileName']);
options = {
'flags': 'a' ,
'encoding' : null,
'mode' : 0666
}
var appendStream = fs.createWriteStream(query['fileName'],[options]);
console.log("query - ");
console.log(query["appendFlag"]);
request.on('data',function(chunk){
if(query["appendFlag"]=="true"){
console.log("appending");
var bufferGood = appendStream.write(chunk.toString());
if(!bufferGood) request.pause();
}else{
var bufferGood = writeStream.write(chunk.toString());
if(!bufferGood) request.pause();
}
});
request.on('end',function(){
response.writeHead(200);
response.write("\n Content with this url is - \n");
var readStream = fs.createReadStream(query['fileName'],{bufferSize:64*1024});
readStream.on('data',function(chunk){
response.write(chunk.toString());
});
readStream.on('end',function(){
response.write("\n");
response.end();
});
});
writeStream.on('drain',function(){
request.resume();
});
}).listen(8080);
That is changed the flag to the 'a' and it also did not append the data?
Your can use your first variant. But before appendFile() you've opened writeStream for the same query["filename"]. The stream is already opened.
var writeStream = fs.createWriteStream(query['fileName']);
options = {
'flags': 'a' ,
'encoding' : null,
'mode' : 0666
}
var appendStream = fs.createWriteStream(query['fileName'],[options]);
May be it's better to do something like:
var options = {
flags: query.appendFile ? 'w' : 'a'
...
Next: why [options]? You should remove the brackets.
Next: there is no guarantee you'll have filename param in querystring. Please handle this situation.

Routing in Express.js - dynamic number of parameters

I've a concept for create routing with multiple parameters when numbers of it are dynamic, for example:
/v2/ModuleName/Method/Method2/
In Express I want parse it as: Modules.v2.ModuleName.Method.Method2(). When will be just one method, this should be of course Modules.v2.ModuleName.Method(). It's possible to do that?
You can split pathname then lookup the method from your Modules object like this:
// fields = ['v2', 'ModuleName', 'Method', 'Method2']
var method = Modules;
fields.forEach(function (field) {
method = method[field];
})
// call method
console.log(method());
Full code:
var express = require('express'), url = require('url');
var app = express();
Modules = {
method: function () { return 'I am root'},
v2: {
method: function () { return 'I am v2';}
}
};
app.get('/callmethod/*', function (req, res) {
var path = url.parse(req.url).pathname;
// split and remove empty element;
path = path.split('/').filter(function (e) {
return e.length > 0;
});
// remove the first component 'callmethod'
path = path.slice(1);
// lookup method in Modules:
var method = Modules;
path.forEach(function (field) {
method = method[field];
})
console.log(method());
res.send(method());
});
app.listen(3000);
Test on browser:
http://example.com:3000/callmethod/method
"I am root"
http://example.com:3000/callmethod/v2/method
"I am v2"
PS: you can improve this app to support pass params to a method via url:
http://example.com:3000/callmethod/v2/method?param1=hello&param2=word

regarding foodme project in github

hello i have a question regarding the foodme express example over github:
code:
var express = require('express');
var fs = require('fs');
var open = require('open');
var RestaurantRecord = require('./model').Restaurant;
var MemoryStorage = require('./storage').Memory;
var API_URL = '/api/restaurant';
var API_URL_ID = API_URL + '/:id';
var API_URL_ORDER = '/api/order';
var removeMenuItems = function(restaurant) {
var clone = {};
Object.getOwnPropertyNames(restaurant).forEach(function(key) {
if (key !== 'menuItems') {
clone[key] = restaurant[key];
}
});
return clone;
};
exports.start = function(PORT, STATIC_DIR, DATA_FILE, TEST_DIR) {
var app = express();
var storage = new MemoryStorage();
// log requests
app.use(express.logger('dev'));
// serve static files for demo client
app.use(express.static(STATIC_DIR));
// parse body into req.body
app.use(express.bodyParser());
// API
app.get(API_URL, function(req, res, next) {
res.send(200, storage.getAll().map(removeMenuItems));
});
i don't understand where is the api folder. it doesn't exist and i don't understand how information is going in and out from there. i can't find it.
can someone please explain this to me?
another question:
there is a resource for the restaurant
foodMeApp.factory('Restaurant', function($resource) {
return $resource('/api/restaurant/:id', {id: '#id'});
});
and in the restaurant controller there is a query:
var allRestaurants = Restaurant.query(filterAndSortRestaurants);
and the following lines:
$scope.$watch('filter', filterAndSortRestaurants, true);
function filterAndSortRestaurants() {
$scope.restaurants = [];
// filter
angular.forEach(allRestaurants, function(item, key) {
if (filter.price && filter.price !== item.price) {
return;
}
if (filter.rating && filter.rating !== item.rating) {
return;
}
if (filter.cuisine.length && filter.cuisine.indexOf(item.cuisine) === -1) {
return;
}
$scope.restaurants.push(item);
});
// sort
$scope.restaurants.sort(function(a, b) {
if (a[filter.sortBy] > b[filter.sortBy]) {
return filter.sortAsc ? 1 : -1;
}
if (a[filter.sortBy] < b[filter.sortBy]) {
return filter.sortAsc ? -1 : 1;
}
return 0;
});
};
the things that isn't clear to me is:
how is that we are giving the query just a function without even activating it.
as i understand we should have passed the query somthing like:
{id: $routeParams.restaurantId}
but we only passed a reference to a function. that doesn't make any sense.
could someone elaborate on this?
thanks again.
var API_URL = '/api/restaurant';
var API_URL_ID = API_URL + '/:id';
var API_URL_ORDER = '/api/order';
These lines are just defining string constants that are plugged into Express further down. They're not a folder.
app.get(API_URL, function(req, res, next) {
res.send(200, storage.getAll().map(removeMenuItems));
});
So this function call to app.get(API_URL... is telling Express "Look out for GET requests that are pointed at the URL (your app's domain)/api/restaurant, and execute this function to handle such a request."
"api" is not a folder.
Every requests will pass through the app.get method.
This method will respond to the routes /api/restaurant as defined in the API_URL variable.

Resources