I've fairly new to NodeJS, and I'm not sure of the best method or syntax to create an MS SQL query with conditional code. Here's what I want to do, with the query greatly simplified, and using some pseudocode:
// #route GET /api/flow/data/references
async function getDataReferences(req, res) {
const { station, type } = req.query
let pool
try {
pool = await sql.connect(config)
const { recordset } = await pool
.request()
.input('station', sql.NVarChar(50), station).query`
SELECT Reference
FROM TABLE
WHERE Status = 'Done' ` +
if(type === 1) {
`AND Station_1 = #station`
} else if(type === 2) {
`AND Station_2 = #station`
} else {
`AND Station_3 = #station`
}
+ `AND Process = 5`
const processedData = recordset.map((item) => item.Reference)
res.json(processedData)
} catch (error) {
console.log(
`ERROR with Station: ${station} with Type: ${type}`,
error.message,
new Date()
)
res.status(500).json({ message: error.message })
} finally {
await pool.close()
}
}
Depending on the value of "type" supplied to the function, I want the query to reference a different DB column.
UPDATE:
So I've found that the following works, although arguably the formatting isn't quite as nice.
// #route GET /api/flow/data/references
async function getDataReferences(req, res) {
const { station, type } = req.query
let station_column
if(type === 1) {
station_column = 'AND Station_1 = #station'
} else if(type === 2) {
station_column = 'AND Station_2 = #station'
} else {
station_column = 'AND Station_3 = #station'
}
let query = `
SELECT Reference
FROM TABLE
WHERE Status = 'Done'
${station_column}
AND Process = 5`
let pool
try {
pool = await sql.connect(config)
const { recordset } = await pool
.request()
.input('station', sql.NVarChar(50), station).query(query)
const processedData = recordset.map((item) => item.Reference)
res.json(processedData)
} catch (error) {
console.log(
`ERROR with Station: ${station} with Type: ${type}`,
error.message,
new Date()
)
res.status(500).json({ message: error.message })
} finally {
await pool.close()
}
}
I tried just using the template literal substitutions directly in the query, but that wouldn't work. (Perhaps for reasons stated here: https://github.com/tediousjs/node-mssql#es6-tagged-template-literals )
If I don't get any better answer, I'll post this as the answer; but would like to know if there's a best practice method for doing this.
Seems like this works, and isn't overly complicated:
// #route GET /api/flow/data/references
async function getDataReferences(req, res) {
const { station, type } = req.query
let station_column
if(type === 1) {
station_column = 'AND Station_1 = #station'
} else if(type === 2) {
station_column = 'AND Station_2 = #station'
} else {
station_column = 'AND Station_3 = #station'
}
let query = `
SELECT Reference
FROM TABLE
WHERE Status = 'Done'
${station_column}
AND Process = 5`
let pool
try {
pool = await sql.connect(config)
const { recordset } = await pool
.request()
.input('station', sql.NVarChar(50), station).query(query)
const processedData = recordset.map((item) => item.Reference)
res.json(processedData)
} catch (error) {
console.log(
`ERROR with Station: ${station} with Type: ${type}`,
error.message,
new Date()
)
res.status(500).json({ message: error.message })
} finally {
await pool.close()
}
}
Essentially, create the full query string in advance, including the parameters to bind, and then pull that whole string in as the query.
the code below performs a select on a sql server database, when the code is executed the following error is generated, i have this error: TypeError: parameter.value.getTime is not a function
Input Parameter:
IdUtente=2
dataInizio=01-07-2018
datafine=02-09-2018
Date:
01-08-2018
01-10-2018
01-11-2018
02-01-2019
02-08-2018
JavaScript:
async function CaricaDataeTotaleOre(IdUtente,dataInizio,datafine) {
console.log("Carica Data e Totale Ore -- IdUtente: "+IdUtente+"\n Data Inizio: "+dataInizio+"\n Data Fine: "+datafine);
var data = [];
var query = "SET LANGUAGE 'Italian' SELECT Distinct CONVERT(varchar(10), DataCreazione, 105) as Data FROM Marcatura inner join Utente on Utente.IdUtente = Marcatura.IdUtente where Utente.IdUtente = #IdUtente and(CONVERT(VARCHAR(10),Marcatura.DataCreazione,103) between #Start and #End) ";
const ret = await new Promise((resolve, reject) => {
new sql.ConnectionPool(DbConfig.config).connect().then(pool => {
return pool.request().input('IdUtente', sql.Int, IdUtente).input('Start', sql.DateTime, dataInizio).input('End', sql.DateTime, datafine).query(query)
}).then(result => {
resolve(result);
sql.close();
}).catch(err => {
ManageError.SendError("Errore CaricaDataeTotaleOre con : " + IdUtente + "\n Errore: " + err);
reject(err)
sql.close();
});
});
for (var i = 0; i < ret.recordset.length; i++) {
data.push({
Data: ret.recordset[i].Data
})
}
return data;
}
I'd like to send base64 image as an attachment to a trello card through the API
POST /1/cards/[card id or shortlink]/attachments
There's a file field but it does not specify how should look the data there.
Refs: https://developers.trello.com/advanced-reference/card#post-1-cards-card-id-or-shortlink-attachments
Any idea?
Short answer: Trello's API only works to attach binary data via multipart/form-data. Examples below.
Long answer:
The Trello API to add attachments and images is frustratingly under-documented. They do have a coffeescript Client.js for those of us using Javascript to simplify all the basic operations: https://trello.com/1/client.coffee
Using the vanilla Client.js file I have been able to attach links and text files. While the CURL example shows pulling a binary file in from a hard drive, that doesn't work for those of us completely on a server or client where we don't have permissions to create a file.
From a LOT of trial and error, I've determined all binary data (images, documents, etc.) must be attached using multipart/form-data. Here is a jQuery snippet that will take a URL to an item, get it into memory, and then send it to Trello.
var opts = {'key': 'YOUR TRELLO KEY', 'token': 'YOUR TRELLO TOKEN'};
var xhr = new XMLHttpRequest();
xhr.open('get', params); // params is a URL to a file to grab
xhr.responseType = 'blob'; // Use blob to get the file's mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData(); // this is the formdata Trello needs
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
I have submitted a new coffeescript for Trello developer support to consider uploading to replace Client.js. It adds a "Trello.upload(url)" that does this work.
I've also attached here for convenience in JS form.
// Generated by CoffeeScript 1.12.4
(function() {
var opts={"version":1,"apiEndpoint":"https://api.trello.com","authEndpoint":"https://trello.com"};
var deferred, isFunction, isReady, ready, waitUntil, wrapper,
slice = [].slice;
wrapper = function(window, jQuery, opts) {
var $, Trello, apiEndpoint, authEndpoint, authorizeURL, baseURL, collection, fn, fn1, i, intentEndpoint, j, key, len, len1, localStorage, location, parseRestArgs, readStorage, ref, ref1, storagePrefix, token, type, version, writeStorage;
$ = jQuery;
key = opts.key, token = opts.token, apiEndpoint = opts.apiEndpoint, authEndpoint = opts.authEndpoint, intentEndpoint = opts.intentEndpoint, version = opts.version;
baseURL = apiEndpoint + "/" + version + "/";
location = window.location;
Trello = {
version: function() {
return version;
},
key: function() {
return key;
},
setKey: function(newKey) {
key = newKey;
},
token: function() {
return token;
},
setToken: function(newToken) {
token = newToken;
},
rest: function() {
var args, error, method, params, path, ref, success;
method = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
ref = parseRestArgs(args), path = ref[0], params = ref[1], success = ref[2], error = ref[3];
opts = {
url: "" + baseURL + path,
type: method,
data: {},
dataType: "json",
success: success,
error: error
};
if (!$.support.cors) {
opts.dataType = "jsonp";
if (method !== "GET") {
opts.type = "GET";
$.extend(opts.data, {
_method: method
});
}
}
if (key) {
opts.data.key = key;
}
if (token) {
opts.data.token = token;
}
if (params != null) {
$.extend(opts.data, params);
}
if (method === 'UPLOAD' && typeof (params) === "string" && params.length > 5) {
var xhr = new XMLHttpRequest();
xhr.open('get', params);
xhr.responseType = 'blob'; // Use blob to get the mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData();
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
} else {
return $.ajax(opts);
}
},
authorized: function() {
return token != null;
},
deauthorize: function() {
token = null;
writeStorage("token", token);
},
authorize: function(userOpts) {
var k, persistToken, ref, regexToken, scope, v;
opts = $.extend(true, {
type: "redirect",
persist: true,
interactive: true,
scope: {
read: true,
write: false,
account: false
},
expiration: "30days"
}, userOpts);
regexToken = /[&#]?token=([0-9a-f]{64})/;
persistToken = function() {
if (opts.persist && (token != null)) {
return writeStorage("token", token);
}
};
if (opts.persist) {
if (token == null) {
token = readStorage("token");
}
}
if (token == null) {
token = (ref = regexToken.exec(location.hash)) != null ? ref[1] : void 0;
}
if (this.authorized()) {
persistToken();
location.hash = location.hash.replace(regexToken, "");
return typeof opts.success === "function" ? opts.success() : void 0;
}
if (!opts.interactive) {
return typeof opts.error === "function" ? opts.error() : void 0;
}
scope = ((function() {
var ref1, results;
ref1 = opts.scope;
results = [];
for (k in ref1) {
v = ref1[k];
if (v) {
results.push(k);
}
}
return results;
})()).join(",");
switch (opts.type) {
case "popup":
(function() {
var authWindow, height, left, origin, receiveMessage, ref1, top, width;
waitUntil("authorized", (function(_this) {
return function(isAuthorized) {
if (isAuthorized) {
persistToken();
return typeof opts.success === "function" ? opts.success() : void 0;
} else {
return typeof opts.error === "function" ? opts.error() : void 0;
}
};
})(this));
width = 420;
height = 470;
left = window.screenX + (window.innerWidth - width) / 2;
top = window.screenY + (window.innerHeight - height) / 2;
origin = (ref1 = /^[a-z]+:\/\/[^\/]*/.exec(location)) != null ? ref1[0] : void 0;
authWindow = window.open(authorizeURL({
return_url: origin,
callback_method: "postMessage",
scope: scope,
expiration: opts.expiration,
name: opts.name
}), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
receiveMessage = function(event) {
var ref2;
if (event.origin !== authEndpoint || event.source !== authWindow) {
return;
}
if ((ref2 = event.source) != null) {
ref2.close();
}
if ((event.data != null) && /[0-9a-f]{64}/.test(event.data)) {
token = event.data;
} else {
token = null;
}
if (typeof window.removeEventListener === "function") {
window.removeEventListener("message", receiveMessage, false);
}
isReady("authorized", Trello.authorized());
};
return typeof window.addEventListener === "function" ? window.addEventListener("message", receiveMessage, false) : void 0;
})();
break;
default:
window.location = authorizeURL({
redirect_uri: location.href,
callback_method: "fragment",
scope: scope,
expiration: opts.expiration,
name: opts.name
});
}
},
addCard: function(options, next) {
var baseArgs, getCard;
baseArgs = {
mode: 'popup',
source: key || window.location.host
};
getCard = function(callback) {
var height, left, returnUrl, top, width;
returnUrl = function(e) {
var data;
window.removeEventListener('message', returnUrl);
try {
data = JSON.parse(e.data);
if (data.success) {
return callback(null, data.card);
} else {
return callback(new Error(data.error));
}
} catch (error1) {}
};
if (typeof window.addEventListener === "function") {
window.addEventListener('message', returnUrl, false);
}
width = 500;
height = 600;
left = window.screenX + (window.outerWidth - width) / 2;
top = window.screenY + (window.outerHeight - height) / 2;
return window.open(intentEndpoint + "/add-card?" + $.param($.extend(baseArgs, options)), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
};
if (next != null) {
return getCard(next);
} else if (window.Promise) {
return new Promise(function(resolve, reject) {
return getCard(function(err, card) {
if (err) {
return reject(err);
} else {
return resolve(card);
}
});
});
} else {
return getCard(function() {});
}
}
};
ref = ["GET", "PUT", "POST", "DELETE", "UPLOAD"];
fn = function(type) {
return Trello[type.toLowerCase()] = function() {
return this.rest.apply(this, [type].concat(slice.call(arguments)));
};
};
for (i = 0, len = ref.length; i < len; i++) {
type = ref[i];
fn(type);
}
Trello.del = Trello["delete"];
ref1 = ["actions", "cards", "checklists", "boards", "lists", "members", "organizations", "lists"];
fn1 = function(collection) {
return Trello[collection] = {
get: function(id, params, success, error) {
return Trello.get(collection + "/" + id, params, success, error);
}
};
};
for (j = 0, len1 = ref1.length; j < len1; j++) {
collection = ref1[j];
fn1(collection);
}
window.Trello = Trello;
authorizeURL = function(args) {
var baseArgs;
baseArgs = {
response_type: "token",
key: key
};
return authEndpoint + "/" + version + "/authorize?" + $.param($.extend(baseArgs, args));
};
parseRestArgs = function(arg) {
var error, params, path, success;
path = arg[0], params = arg[1], success = arg[2], error = arg[3];
if (isFunction(params)) {
error = success;
success = params;
params = {};
}
path = path.replace(/^\/*/, "");
return [path, params, success, error];
};
localStorage = window.localStorage;
if (localStorage != null) {
storagePrefix = "trello_";
readStorage = function(key) {
return localStorage[storagePrefix + key];
};
writeStorage = function(key, value) {
if (value === null) {
return delete localStorage[storagePrefix + key];
} else {
return localStorage[storagePrefix + key] = value;
}
};
} else {
readStorage = writeStorage = function() {};
}
};
deferred = {};
ready = {};
waitUntil = function(name, fx) {
if (ready[name] != null) {
return fx(ready[name]);
} else {
return (deferred[name] != null ? deferred[name] : deferred[name] = []).push(fx);
}
};
isReady = function(name, value) {
var fx, fxs, i, len;
ready[name] = value;
if (deferred[name]) {
fxs = deferred[name];
delete deferred[name];
for (i = 0, len = fxs.length; i < len; i++) {
fx = fxs[i];
fx(value);
}
}
};
isFunction = function(val) {
return typeof val === "function";
};
wrapper(window, jQuery, opts);
}).call(this);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
I'm running this SQL query with tedious.js using parameters:
var query = "select * from table_name where id in (#ids)";
request = new sql.Request(query, function(err, rowCount) {
if (err) {
}
});
request.on('row', function(columns) {
});
var id = [1, 2, 3];
request.addParameters('ids', TYPES.Int, id);
connection.execSql(request);
because I am looking for items that matches the ID provided with where ... in ... clause, I need to pass in an array. However, there is no TYPES.Array. How do I this properly?
for this query, i think you'll just have to manually build the entire sql string. the TYPES enum values are for the datatypes in the database, not in your JavaScript code.
//you can like this:
var userIds = result.map(function (el) {
return el.UserId;
}).join(',');
var params = [{
name: 'userIds',
type: TYPES.VarChar,
value: userIds,
options: null}];
var querySql = ['SELECT COUNT([MomentId]) FROM [T_Moment]',
'WHERE [RecordStatus] = ', sysConst.recordStatus.activation, " AND CHARINDEX(','+RTRIM([UserId])+',' , ','+ #userIds +',')>0 "].join(' ');
dbHelper.count(querySql, params, function (err, result) {
if (err) {
callback('error--');
} else {
callback(null, result);
}
});
Try creating the in clause parameters for query dynamically.
// create connection
let ids = [1, 2, 3];
let inClauseParamters = createInClauseParameters();
let query = `select * from table_name where id in (${inClauseParamters})`;
let request = new Request(query, (err, rowCount) => {
if (err) { /* handle error */ }
});
request.on('row', (columns) => { /* get row */});
request = addRequestParameters(ids, request);
connection.execSql(request);
function createInClauseParameters(values) {
return values.map((val, index) => `#Value${index}`).join(',');
}
function addRequestParameters(values, request) {
values.forEach((val, index) => {
request.addParameter(`Value${index}`, TYPES.VarChar, val);
});
return request;
}
I am creating a custom authentication via API on Azure Mobile Service, and picked based on this answer:
Registering and login users in Azure Mobile Services
Then joined with the code of the previous link to create the authentication token.
But I get "internal server error" when I invoke the API. The error happens in here: "...results.length..."
var crypto = require('crypto');
var iterations = 1000;
var bytes = 32;
var aud = "Custom";
var masterKey = "wkeHEoWUaPJSHsSOcWgmVLOZbIpeeg92";
var _request;
var _response;
exports.post = function(request, response) {
var user = request.body.userName;
var pass = request.body.password;
_request = request;
_response = response
validateUserNamePassword(user, pass, function(error, userId, token) {
if (error) {
response.send(401, { error: "Unauthorized" });
} else {
response.send(200, { user: userId, token: token });
}
});
}
function validateUserNamePassword(user, pass, funcao){
var accounts = _request.service.tables.getTable('account');
accounts
.where({ userid : user })
.read({
success: function(results)
{
if (results.length === 0)
{
_response.send(401, { error: "Unauthorized1" });
console.log("Incorrect username or password");
_request.respond(401, "Incorrect username or password");
}
else
_response.send(401, { error: "Unauthorized2" });
var account = results[0];
hash(item.password, account.salt, function(err, h) {
var incoming = h;
if (slowEquals(incoming, account.password)) {
var expiry = new Date().setUTCDate(new Date().getUTCDate() + 30);
var userId = aud + ":" + account.id;
_request.respond(200, {
userId: userId,
token: zumoJwt(expiry, aud, userId, masterKey)
});
}
else {
_request.respond(401, "Incorrect username or password");
}
});
}
}
});
}
function hash(text, salt, callback) {
crypto.pbkdf2(text, salt, iterations, bytes, function(err, derivedKey){
if (err) { callback(err); }
else {
var h = new Buffer(derivedKey).toString('base64');
callback(null, h);
}
});
}
function slowEquals(a, b) {
var diff = a.length ^ b.length;
for (var i = 0; i < a.length && i < b.length; i++) {
diff |= (a[i] ^ b[i]);
}
return diff === 0;
}
function zumoJwt(expiryDate, aud, userId, masterKey) {
var crypto = require('crypto');
function base64(input) {
return new Buffer(input, 'utf8').toString('base64');
}
function urlFriendly(b64) {
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(new RegExp("=", "g"), '');
}
function signature(input) {
var key = crypto.createHash('sha256').update(masterKey + "JWTSig").digest('binary');
var str = crypto.createHmac('sha256', key).update(input).digest('base64');
return urlFriendly(str);
}
var s1 = '{"alg":"HS256","typ":"JWT","kid":0}';
var j2 = {
"exp":expiryDate.valueOf() / 1000,
"iss":"urn:microsoft:windows-azure:zumo",
"ver":1,
"aud":aud,
"uid":userId
};
var s2 = JSON.stringify(j2);
var b1 = urlFriendly(base64(s1));
var b2 = urlFriendly(base64(s2));
var b3 = signature(b1 + "." + b2);
return [b1,b2,b3].join(".");
}
I'm invoking like this:
try
{
var loginInput = new JObject();
loginInput.Add("userName", "breno");
loginInput.Add("password", "test");
var loginResult = await LoginAuthenticationService.InvokeApiAsync("login", loginInput);
LoginAuthenticationService.CurrentUser = new MobileServiceUser((string)loginResult["user"]);
LoginAuthenticationService.CurrentUser.MobileServiceAuthenticationToken = (string)loginResult["token"];
}
catch (MobileServiceInvalidOperationException e)
{
var exception = e;
}
If you're getting this error "Failed to load script file 'login.js': SyntaxError: Unexpected token }" as seen in the comments, there is a syntax issue with your script. You'll need to go through your scripts and figure out where the issue is at.