Grunt redirect rest call to file - javascript

I am using grunt connect server with all those livereload plugins. Developing process was very fast till I added some rest calls. I decided to make some example JSON files which ll be rest calls answers.
So I need to redirect every rest call to some static folder(important I ll put it in target folder) so it ll be outside the Angular folder.
So I need some plugin which transfer calls like:
http.get(localhost:port/x/y/name) to target/jsons_examples/x/y/name.json
http.get(localhost:port/z/name) to target/jsons_examples/z/name.json
grunt file:(1 aplication server, 1 mocking webservices)
grunt.initConfig({
connect: {
all: {
options: {
port: 10100,
hostname: "0.0.0.0",
livereload: true
}
},
webservices_mock: {
options: {
port: 8081,
hostname: "0.0.0.0",
middleware: function(connect, options, middlewares) {
middlewares.unshift(function(req, res, next) {
var pattern = new RegExp('\/rest\/[a-zA-Z0-9\/.]+', 'i'),
matches = req.url.match(pattern);
if (!matches) {
return next();
}
req.setEncoding('utf8');
res.writeHead(200, {"Content-Type": "application/json"});
res.write(grunt.file.read(req.url.replace("/rest", "json_contracts") + "/example.json"));
res.end();
});
return middlewares;
}
}
}
},
...
And now i need in web services mock configuration change the path from json_contracts to path outside angular folder smth like: ././././target/json_contracts

You can use middleware option to inject your own URL parser. See the comments to understand how it works:
grunt.initConfig({
connect: {
all: {
options:{
port: 8080,
base: dir_to_angular_main_folder
hostname: "localhost",
middleware: function(connect, options, middlewares) {
middlewares.unshift(function(req, res, next) {
// pattern is a RegExp which is going to find the redirected url
var pattern = new RegExp('^/x/y/([0-9A-Z]+)$', 'i'),
// matches is a result which is
// - undefined when no match found
// - an array with two values when found:
// 0: the full string
// 1: the string inside of the brackets (the file name)
matches = req.url.match(pattern);
// if your url does not match skip the bottom part
if (!matches) return next();
// this runs only if match is found, set up the redirect header
// up to you to decide whether it is 301 or 302
// I would keep it 302 for dev purposes (browsers won't cache it)
res.writeHead(302, {
Location: '/target/jsons_examples/x/y/' + matches[1] + '.json'
});
// send the response
res.end();
});
return middlewares;
}
}
}
}
});
I guess you will still need to change the pattern to the one you need (x / y / target don't sound as real names).
One can say you can do it easier without matching like this ([0-9A-Z]+) and using the matches here '/target/jsons_examples/x/y/' + matches[1] + '.json' and he will be right. It was made with a name only because it is more flexible, e.g. when you have a folder in between x and y etc. Anyway, as it was said before, you can also make it simpler and just use the following:
res.writeHead(302, {
Location: '/target/jsons_examples' + req.url + '.json'
});
I could not test it, maybe it gives you some errors, but still I hope this is good enough to give an idea what and how to do.
EDIT
Well, according to the fact that your JSON files are not visible from the webserver side (the root folder is out of scope) you can do some kind of url rewriting instead of redirect:
var fs = require('fs');
grunt.initConfig({
connect: {
all: {
options:{
port: 8080,
base: dir_to_angular_main_folder
hostname: "localhost",
middleware: function(connect, options, middlewares) {
middlewares.unshift(function(req, res, next) {
// pattern is a RegExp which is going to find the redirected url
var pattern = new RegExp('^/x/y/([0-9A-Z]+)$', 'i'),
// matches is a result which is
// - undefined when no match found
// - an array with two values when found:
// 0: the full string
// 1: the string inside of the brackets (the file name)
matches = req.url.match(pattern);
// if your url does not match skip the bottom part
if (!matches) return next();
// this runs only if match is found, set up the redirect header
// reads the file content and sends as a response
res.end(fs.readFileSync('/<some-absolute-path>/target/jsons_examples/x/y/' + matches[1] + '.json', 'utf8'));
});
return middlewares;
}
}
}
}
});
Note that you need to include the Nodejs standard fs module on the top of your Gruntfile to make it work. This is again just a prototype, change the paths according to your requirements. Maybe you would also need to write the mime-type as a header of your response, there are already many answers on how to do it in Node as well.

Related

Skip Coffeescript tests depending on software version

I have a coffeescript file called archive.spec.coffee.
It begins with:
describe 'Archive', ->
and all tests are within the describe.
I want to check for a software version using a http call and depending on what that is, I'd like to either execute or skip the tests.
I think I need to do something like this but so far it isn't working:
req = https.get {host: "https://path/to/endpoint"}
if(req.version == 1.2.3)
this.skip
else
describe 'Archive', ->
Can anybody tell me how to correctly do this?
Additional info
I've came up with this:
beforeEach ->
req = request.get {
url: 'https://path/to/endpoint',
auth: {
user: 'admin',
pass: 'password'
},
json: true
},
(err, res, body) ->
version = body.responseData.devices[0].versions[0].version
if (version.indexOf('1.2.3') == -1)
done()
else
this.skip()
describe 'Archive', ->
My problem now is that if version is 1.2.3, version.indexOf('1.2.3') returns 0, the else is entered but the tests are not skipped.
Latest update
Now I've come up with this:
before (done) ->
req = request.get {
url: 'https://path/to/endpoint',
auth: {
user: 'admin',
pass: 'password'
},
json: true
},
(err, res, body) ->
version = body.responseData.devices[0].versions[0].version
if (version.indexOf('1.2.3') != -1)
skip = true
done()
else
done()
describe 'Archive', ->
it 'my_test', ->
if (skip)
this.skip()
If I test this with version 1.2.3 I can see that the skip is set to true in the before function. done() returns to my_test but if (skip) is false. Obviously this is because skip is set to true in a different function. How do I get it so that the before returns the value of skip to my test?
I have a json formatted config file so I added a version key which takes an environment variable containing the version. Once in the config I can then use it as I like.

Making .local resolve to IP address AND port (mdns)

I'm using the multicast-dns node module to attempt making this work.
Looking up custom.local in the browser gives me the console message I setup, but I'm unable to see my actual server running (which is doing so at localhost:12345, where 12345 is a dynamic number). I want to be able to see my local server when visiting custom.local. Is this possible?
Here's some code:
mdns.on("query", query => {
if (query.questions[0] && query.questions[0].name === "custom.local") {
console.log(query);
mdns.respond({
answers: [
{
name: "custom.local",
type: "SRV",
data: {
port: n.get("p"), // dynamic port
weight: 0,
priority: 10,
target: ip // local IP
}
}, {
name: "custom.local",
type: "A",
data: ip,
ttl: 300
}
]
});
}
});
EDIT: I can connect to my local server just fine, that wasn't an issue.
Quoting cfreak:
You can't put port numbers in DNS. DNS is only for looking up an IP by name. For your browser to see it by the name alone you need a proxy program in front of your service or you need to run the service itself on port 80. Port numbers really shouldn't be dynamic. You should specify it in the setup of your service.
That answers my question and offers next steps. Thanks!
UPDATE: Figured out what I was trying to do. Here's some code!
FOUND A SOLUTION, WOOP WOOP!
I'm using this module, but tweaked the source a bit (only because I have dynamic ports, because I feel like it).
/* jshint undef: true, unused: true, esversion: 6, node: true */
"use strict";
//
// G E T
// P A C K A G E S
import express from "express";
import http from "http";
import local from "./server/local";
const n = express();
n.get("/", (req, res) => {
res.send("Welcome home");
});
//
// L A U N C H
const server = http.createServer(n);
server.listen(0, () => {
const port = server.address().port;
local.add(port, "custom.local");
});
Hope this helps you as well, future Internet searcher! :D
Don't let negative folks on other SE sites bring you down. :virtual fist bump:

JS AWS SDK - copyObject SignatureDoesNotMatch error

I have an application using Node and the AWS-SDK package. I am copying objects from one bucket to another using the copyObject method. I'm getting an error that says SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
I've been able to successfully run the code on my local machine and it copies the files from one bucket to another. The error occurs on our AWS server, which I deployed the application to. The full error is:
{ [SignatureDoesNotMatch: The request signature we calculated does not
match the signature you provided. Check your key and signing method.]
message: 'The request signature we calculated does not match the signature you provided. Check your key and signing method.',
code: 'SignatureDoesNotMatch',
region: null,
time: Mon Jul 11 2016 12:11:36 GMT-0400 (EDT),
requestId: <requestId>,
extendedRequestId: <extendedRequestId>,
cfId: undefined,
statusCode: 403,
retryable: false,
retryDelay: 66.48076744750142 }
Also, I'm able to perform the listObjects command. The error is only happening on copyObject.
So far, I've tried
setting correctClockSkew to true
checked the servers time (same as local computer)
checked the key/secret (loading from a config file and is working locally)
checked the file names (there are no strange characters. Alphanumeric, '.', '-' and '/')
Here is the code causing the problem:
AWS.config.update({
accessKeyId: <accessKeyId>,
secretAccessKey: <secretAccessKey>,
correctClockSkew: true
});
var s3 = new AWS.S3();
var params = {
Bucket: <bucket>,
Prefix: <prefix>
};
s3.listObjects(params, function(err, data) {
if (data.Contents.length) {
async.each(data.Contents, function(file, cb) {
var file_name = file.Key.substr(file.Key.indexOf('/')+1);
var copy_params = {
Bucket: <bucket2>,
CopySource: <bucket> + '/' + file.Key,
Key: file_name,
ACL: 'public-read'
};
s3.copyObject(copy_params, function(copyErr, copyData){
if (copyErr) {
console.log('Error:', copyErr);
}
else {
cb();
}
});
}, function(err){
...
}
});
} else {
...
}
});
Not sure if you've found a solution to this or not, but this was an issue raised on github and the solution seems to simply URL encode your CopySource parameter with encodeURI():
https://github.com/aws/aws-sdk-js/issues/1949

update URL string with different port

In my program I got URL like following format,
when I use the url.parse nothing happen,I need to change the port ,how it suggested do do that ?
The port is after the colon %3 55123 and I need to change it to 8080
http%3A%2F%2Fmo-de73.mo.bmw.corp%3A55123%2Flogin%2Fcallback&client_id=s2.app
The number can be change(any valid port) so I cannot use the replace
Btw there is parser which change the format from %3 to colon and and %2F to slash as standard URL output?
Before you can parse it, you must decode it. You can do this using the built-in decodeURIComponent function.
var url = require('url');
var encodedUrl = 'http%3A%2F%2Fmo-de73.mo.bmw.corp%3A55123%2Flogin%2Fcallback&client_id=s2.app';
var decodedUrl = decodeURIComponent(encodedUrl);
//=> 'http://mo-de73.mo.bmw.corp:55123/login/callback&client_id=s2.app'
var parts = url.parse(decodedUrl);
/* =>
{ protocol: 'http:',
slashes: true,
auth: null,
host: 'mo-de73.mo.bmw.corp:55123',
port: '55123',
hostname: 'mo-de73.mo.bmw.corp',
hash: null,
search: null,
query: null,
pathname: '/login/callback&client_id=s2.app',
path: '/login/callback&client_id=s2.app',
href: 'http://mo-de73.mo.bmw.corp:55123/login/callback&client_id=s2.app' }
*/
parts.port = 8080;
//=> 8080
delete parts.host;
//=> true
url.format(parts);
//=> 'http://mo-de73.mo.bmw.corp:8080/login/callback&client_id=s2.app'
The last part there is a bit of a hack. After you update the port, your parts object will look like this
{
// ...
host: 'mo-de73.mo.bmw.corp:55123',
port: '8080',
hostname: 'mo-de73.mo.bmw.corp',
// ...
}
The hostname is correct, but the host still has the port hard-coded in there. If you just remove the host using delete parts.host, url.format will automatically create the correct host using {hostname}:{port}.
Maybe there's a better way to do it, but at least this should get you started.
Try this
var url="http%3A%2F%2Fmo-de73.mo.bmw.corp%3A55123%2Flogin%2Fcallback&client_id=s2.app";
url=url.replace('%3A55123',":8080");
var decodedUrl = decodeURIComponent(url);
//decodedUrl=http://mo-de73.mo.bmw.corp:8080/login/callback&client_id=s2.app

Rendering a PDF using Meteor and Iron Router

I have a pdf file located here:
/server/.files/users/test.pdf
When I display a link on a page, I'd like for the user to be able to click the link, and for the pdf to be rendered on the screen.
I've read through this SO post in particular, and others, but cannot seem to get things to work: SO Link
I tried using an IR route server side, but every time I try even something simple I get the following error:
Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions. at Object.Meteor.userId
(packages/accounts-base/accounts_server.js:19:1) at Object.Meteor.user
(packages/accounts-base/accounts_server.js:24:1) at [object
Object].Router.onBeforeAction.except
(app/both/3-router/routes.js:10:15) at
packages/iron:router/lib/router.js:277:1 at [object
Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
at [object Object].hookWithOptions
(packages/iron:router/lib/router.js:276:1) at boundNext
(packages/iron:middleware-stack/lib/middleware_stack.js:251:1) at
runWithEnvironment (packages/meteor/dynamics_nodejs.js:108:1) at
packages/meteor/dynamics_nodejs.js:121:1 at [object Object].dispatch
(packages/iron:middleware-stack/lib/middleware_stack.js:275:1)
Line: #10 in my router.js file is the first if statement here:
Router.onBeforeAction(function () {
if (!Meteor.user() || Meteor.loggingIn()) {
this.redirect('welcome.view'); } else {
Meteor.call("userFileDirectory", function (error, result) {
if (error)
throw error;
else
console.log(result);
});
this.next();
} }, { except: ['welcome.view'] });
I tried this:
Router.map(function() {
this.route('serverFile', {
where: 'server',
path: /^\/uploads_url_prefix\/(.*)$/,
action: function() {
var filePath = process.env.PWD + '/.files/users/' + this.params[1];
var data = fs.readFileSync(filePath);
this.response.writeHead(200, {
'Content-Type': 'image'
});
this.response.write(data);
this.response.end();
}
}); });
But I'm not sure what to put in the path.
With process.env.PWD you are in the directory of your meteor project.
so you should be able to access your file like this:
var file = process.env.PWD + "/server/.files/users/test.pdf"
To use the fs package of node you also need to include it and you need to be on the server:
Router.route('/pdf', function() {
var filePath = process.env.PWD + "/server/.files/users/test.pdf";
var fs = Meteor.npmRequire('fs');
var data = fs.readFileSync(filePath);
this.response.write(data);
this.response.end();
}, {
where: 'server'
});
Make sure to this package to your project (https://atmospherejs.com/meteorhacks/npm)
meteor add meteorhacks:npm
I tested it and it is working like a charm!

Categories