I want to use fast-xml-parser to validate a SOAP response from a Postman request. Based on various articles, I implemented fetch cdn and put it in a variable method.
So this is my script, postman will call the cdn endpoint to get the fast-xml-parser package:
https://cdnjs.cloudflare.com/ajax/libs/fast-xml-parser/4.0.7/fxparser.js
pm.test("Validate XML", function () {
pm.sendRequest("https://cdnjs.cloudflare.com/ajax/libs/fast-xml-parser/4.0.7/fxparser.js", (error, res) => {
if (error || res.code !== 200) {
pm.expect.fail('Could not load external library');}
const fastxmlvalidator = res.text();
console.log(fastxmlvalidator)
console.log(fastxmlvalidator.XMLValidator)
const result = fastxmlvalidator.XMLValidator.validate(pm.response.text(), {
allowBooleanAttributes: true
});
console.log(result)
});
})
And store it in fastxmlvalidator constant. Now if I console log it, postman will return all the content stored in that cdn. And when I tried to use the constructor, it returned an "undefined". Hence, I cannot retrieve the function validate from it.
Here's my code in VSCode:
I set the package in the fastxmlvalidator, have a sample SOAP in "XMLdata" and execute it as follow:
const fastxmlvalidator = require("fast-xml-parser");
console.log(fastxmlvalidator)
const XMLdata = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">"+
" <SOAP-ENV:Header/>"+
" <SOAP-ENV:Body>"+
" <Message xmlns=\"http://schemas.com.au/message/Message.xsd\">"+
" <Control>"+
" <Environment>DEV</Environment>"+
" </Control>"+
" </Message>"+
" </SOAP-ENV:Body>"+
"</SOAP-ENV:Envelope>"
const result = fastxmlvalidator.XMLValidator.validate(XMLdata, {
allowBooleanAttributes: true
});
console.log(result)
It should return a boolean of true, which means it's a valid XML.
Any suggestions would be much appreciated it!
Related
I'm trying to make an app that can send payments to PayBill numbers with Safaricom's "Lipa Na M-Pesa" (a Kenyan thing). The call is a POST request to URL:
https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest
with header:
{
'Host': 'sandbox.safaricom.co.ke',
'Authorization': 'Bearer ${await mpesaAccessToken}',
'Content-Type': 'application/json',
}
and body:
{
"BusinessShortCode": "$businessShortCode",
"Password": "${generateLnmPassword(timeStamp)}",
"Timestamp": "$timeStamp",
"TransactionType": "CustomerPayBillOnline",
"Amount": "10",
"PartyA": "$userPhoneNumber",
"PartyB": "$businessShortCode",
"PhoneNumber": "$userPhoneNumber",
"CallBackURL": "?????????????????????????????",
"AccountReference": "account",
"TransactionDesc": "test",
}
I've received an access token, generated a password and made the call successfully, except for that CallBackURL thing... The M-Pesa docs describe their callback like this:
CallBackURL
This is the endpoint where you want the results of the transaction delivered. Same rules for Register URL API callbacks apply.
all API callbacks from transactional requests are POST requests, do not expect GET requests for callbacks. Also, the data is not formatted into application/x-www-form-urlencoded format, it is application/json, so do not expect the data in the usual POST fields/variables of your language, read the results directly from the incoming input stream.
(More info here, but you may need to be logged in: https://developer.safaricom.co.ke/get-started see "Lipa na M-Pesa")
My app is hosted on Firebase Cloud Firestore. Is there any way I can create a callback URL with them that will receive their callback as a document in a Firestore collection?...
Or would this be impossible, given that they would need authorization tokens and stuff to do so... and I can't influence what headers and body M-Pesa will send?
(PS Btw, I code in Flutter/Dart so plz don't answer in Javascript or anything! I'll be clueless... :p Flutter/Dart or just plain text will be fine. Thanks!)
Is there any way I can create a callback URL with them that will
receive their callback as a document in a Firestore collection?...
The most common way to do that in the Firebase ecosystem is to write an HTTPS Cloud Function that will be called by the Safaricom service.
Within the Cloud Function you will be able to update the Firestore document, based on the content of the POST request.
Something like:
exports.safaricom = functions.https.onRequest((req, res) => {
// Get the header and body through the req variable
// See https://firebase.google.com/docs/functions/http-events#read_values_from_the_request
return admin.firestore().collection('...').doc('...').update({ foo: bar })
.then(() => {
res.status(200).send("OK");
})
.catch(error => {
// ...
// See https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3
})
});
I did note that you ask us to not "answer in Javascript or anything" but in Flutter/Dart, but I don't think you will able to implement that in Flutter: you need to implement this webhook in an environment that you fully control and that exposes an API endpoint, like your own server or a Cloud Function.
Cloud Functions may seem complex at first sight, but implementing an HTTPS Cloud Functions is not that complicated. I suggest you read the Get Started documentation and watch the three videos about "JavaScript Promises" from the Firebase video series, and if you encounter any problem, ask a new question on SO.
Cloud functions are not Dart-based.
See below solution;
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const parse = require("./parse");
admin.initializeApp();
exports.lmno_callback_url = functions.https.onRequest(async (req, res) => {
const callbackData = req.body.Body.stkCallback;
const parsedData = parse(callbackData);
let lmnoResponse = admin.firestore().collection('lmno_responses').doc('/' + parsedData.checkoutRequestID + '/');
let transaction = admin.firestore().collection('transactions').doc('/' + parsedData.checkoutRequestID + '/');
let wallets = admin.firestore().collection('wallets');
if ((await lmnoResponse.get()).exists) {
await lmnoResponse.update(parsedData);
} else {
await lmnoResponse.set(parsedData);
}
if ((await transaction.get()).exists) {
await transaction.update({
'amount': parsedData.amount,
'confirmed': true
});
} else {
await transaction.set({
'moneyType': 'money',
'type': 'deposit',
'amount': parsedData.amount,
'confirmed': true
});
}
let walletId = await transaction.get().then(value => value.data().toUserId);
let wallet = wallets.doc('/' + walletId + '/');
if ((await wallet.get()).exists) {
let balance = await wallet.get().then(value => value.data().moneyBalance);
await wallet.update({
'moneyBalance': parsedData.amount + balance
})
} else {
await wallet.set({
'moneyBalance': parsedData.amount
})
}
res.send("Completed");
});
Parse function.
const moment = require("moment");
function parse(responseData) {
const parsedData = {};
parsedData.merchantRequestID = responseData.MerchantRequestID;
parsedData.checkoutRequestID = responseData.CheckoutRequestID;
parsedData.resultDesc = responseData.ResultDesc;
parsedData.resultCode = responseData.ResultCode;
if (parsedData.resultCode === 0) {
responseData.CallbackMetadata.Item.forEach(element => {
switch (element.Name) {
case "Amount":
parsedData.amount = element.Value;
break;
case "MpesaReceiptNumber":
parsedData.mpesaReceiptNumber = element.Value;
break;
case "TransactionDate":
parsedData.transactionDate = moment(
element.Value,
"YYYYMMDDhhmmss"
).unix();
break;
case "PhoneNumber":
parsedData.phoneNumber = element.Value;
break;
}
});
}
return parsedData;
}
module.exports = parse;
Function code below
prepay.post('/' , (req, res) => {
req.on("data", function (chunk) {
strdat += chunk;
console.log(strdat);
}).on("end", function()
{
var data = JSON.parse(strdat);
var cryp = crypto.createHash('sha512');
var text = \\ some data;
cryp.update(text);
var hash = cryp.digest('hex');
res.setHeader("Content-Type", "text/json");
res.setHeader("Access-Control-Allow-Origin", "*");
res.end(JSON.stringify(hash));
});
req.on('error', function(err){
console.log(err.message)
});
});
exports.prepay = functions.https.onRequest(prepay);
=================================
this is tried on emulator
in the logs getting ! functions: Your function timed out after ~60s. To configure this timeout, see https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.
\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:660 throw new Error("Function timed out.");
works fine when ran locally with nodejs using node server.js
not sure if req.on supported by firebase will be helpful if I get some reference on req.on in firebase functions
Your event-based sample of code won't work due to the preprocessing of the request that is done by the Firebase Functions SDK. Simply put, all of the 'data' and 'end' events have occurred prior to your code being executed.
Inside of functions.https.onRequest, the request is consumed and parsed according to it's content type automatically as documented here. If your request body is one of the below recognised types, it will be parsed and available as request.body. If you wish to work with the raw buffer of data, it is exposed as a Buffer as request.rawBody.
Content Type Request Body Behavior
application/json '{"name":"John"}' request.body.name equals 'John'
application/octet-stream 'my text' request.body equals '6d792074657874' (the raw bytes of the request; see the Node.js Buffer documentation)
text/plain 'my text' request.body equals 'my text'
application/x-www-form-urlencoded 'name=John' request.body.name equals 'John'
This preprocessing allows you to get to the actual function of your code faster.
prepay.post('/' , (req, res) => {
const data = req.body;
const cryp = crypto.createHash('sha512');
const text = \\ some data;
cryp.update(text);
const hash = cryp.digest('hex');
res.setHeader("Access-Control-Allow-Origin", "*");
res.json(hash);
});
exports.prepay = functions.https.onRequest(prepay);
Many have experience with generic automated tests with Puppeteer. This is mostly about imitating user behavior on web pages and asserting results. But what if you need to test representation of errored state, which you cannot (or don’t know how to) trigger by a user action? For example, we have a form. When user submits the app sends a POST request to the server and shows success messages as soon as the server responds with 200 OK. That is the expected behavior. Yet, in real world the server may happened to be down or respond with Internal Error. Imagine the app covers gratefully this case. But how we would reproduce it with Puppeteer?
Given that Puppeteer provides a lot of possibilities on handling HTTP/S requests, I came up with the following solution. While submitting the form I mock the server response with my own.
Basically I set up an interceptor, which replaces the next request matching a given URL fragment with custom server response:
await bs.mockRequest( "response.json", { "GET", "500 Internal Server Error", "application/javascript", "{ \"status\": \"FAIL\" }", [] });
While it’s watching the network, I can submit the form, e.g. by emulating click on the submit button. The following request will be replaced so that the app under test would treat it as in the case of real server error.
Here the source code of the mockRequest:
class BrowserSession {
// obtaining page context from Puppeteer
// #see https://pptr.dev/#?product=Puppeteer&version=v2.0.0&show=api-puppeteerlaunchoptions
async setup( options ) {
this.browser = await puppeteer.launch( options );
this.context = await this.browser.createIncognitoBrowserContext();
this.page = await this.context.newPage();
}
/**
* Intercept and replace request
* #param {String} url - URL substring to match
* #param {Object} newRespond
*/
async mockRequest( url, { method, status, contentType, newBody, headers }) {
const session = await this.page.target().createCDPSession(),
patterns = [ `*${ url }*` ];
await session.send( "Network.enable" );
await session.send( "Network.setRequestInterception", {
patterns: patterns.map( pattern => ({
urlPattern: pattern,
interceptionStage: "HeadersReceived"
}))
});
session.on( "Network.requestIntercepted", async ({ interceptionId, request, responseHeaders, resourceType }) => {
if ( ( method || "GET" ) !== request.method.toUpperCase() ) {
await session.send( "Network.continueInterceptedRequest", { interceptionId });
return;
}
const newHeaders = [
"Date: " + ( new Date() ).toUTCString(),
"Connection: closed",
"Content-Length: " + newBody.length,
"Content-Type: " + contentType,
...headers
];
await session.send( "Network.continueInterceptedRequest", {
interceptionId,
rawResponse: btoa( `HTTP/1.1 ${ status }\r\n`
+ newHeaders.join('\r\n') + '\r\n\r\n' + newBody )
});
session.detach();
});
}
}
We are using google cloud translation API in our express application.
I am trying to do translations using the client library instead of making an API request every time.
1. What I want to know is how to pass the options like format (text or html) to the api while using the client library?
I can achieve this via making http requests using requestjs like this:
var request = require('request');
var url = 'https://translation.googleapis.com/language/translate/v2';
var options1 = {
q: 'amore mio',
target: 'hi',
format: 'text',
source: 'it',
key: 'my API key'
}
request.post({url:url, qs:options1}, (err, res, body)=> {
if(err) {
console.log('ERR: ', err);
}
console.log('RES: ', res.statusCode);
console.log('Body: ', body);
})
But the sample for using client library shows only this:
const {Translate} = require('#google-cloud/translate');
// Your Google Cloud Platform project ID
const projectId = 'YOUR_PROJECT_ID';
// Instantiates a client
const translate = new Translate({
projectId: projectId,
});
// The text to translate
const text = 'Hello, world!';
// The target language
const target = 'ru';
// Translates some text into Russian
translate
.translate(text, target)
.then(results => {
const translation = results[0];
console.log(`Text: ${text}`);
console.log(`Translation: ${translation}`);
})
.catch(err => {
console.error('ERROR:', err);
});
Is there a way I can pass options like 'format' using the client library?
How can I pass an array of strings to the q attribute (querystring) of the options object in the first method? If I pass an array directly like:
q: ['amore mio', 'grazie']
I get an error message :
RES: 400
Body: {
"error": {
"code": 400,
"message": "Required Text",
"errors": [
{
"message": "Required Text",
"domain": "global",
"reason": "required"
}
]
}
}
With respect to question 2 about passing the array of input arguments, this works fine if you use cURL to send the POST request similar to this example. I have tried it myself with success. I have tried to do different manipulations with your code from snipper 1 with the request library, but it seems as if the request library is not passing the array correctly. I would generally suggest using the client library which can successfully handle arrays in the input text.
Okay after a little research I just tried to pass options object with format and other properties (like source and target language) instead of target, and it worked.
So this can be achieved by:
const options = {
to: target,
format: 'html',
prettyPrint: true
}
translate
.translate(text, options)
.then(results => {
const translation = results[0];
console.log('flag: ', Array.isArray(translation));
console.log(`Text: ${text}`);
console.log(`Translation: ${translation}`);
})
.catch(err => {
console.error('ERROR:', err);
});
Use JSON.stringify
`https://translation.googleapis.com/language/translate/v2?q=${JSON.stringify([array]}`
I am trying to translate the name of a user from english to an indian language using google translate api and storing the data back in realtime database with a cloud function.
This function is invoked by a write to the database, and I am using a HTTP POST request to send a request to the cloud translate api and the response is stored back to the database. My code for the translate request is this.
var translate_options = { method: 'POST',
url: 'https://translation.googleapis.com/language/translate/v2',
qs:
{ key: 'key goes here',
},
form: {
q: fullData.name,
target: "te"
},
};
request(translate_options, function (error, translate_response, translate_body) {
if (error){
console.log("In translating, got an error");
console.log(error);
}
// Query to the database goes here.
});
This code, if tried in my laptop, gives me the correct translation, but if I deploy it as a cloud function, it gives me an error. Very specifically
{ Error: read ECONNRESET
at exports._errnoException (util.js:1020:11)
at TLSWrap.onread (net.js:568:26) code: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read' }
I am on firebase blaze plan, and I am able to sent POST request to my other services, but not a google service.
Can anybody help me with this issue. Thanks in advance.
Edit :
The full code is
var functions = require('firebase-functions');
var admin = require('firebase-admin');
var request = require("request");
admin.initializeApp(functions.config().firebase);
exports.whenUserIsAdded = functions.database.ref('users/{companyId}/{uid}').onCreate(event => {
var fullData = event.data.val();
var lang_code = {
"bengali": "bn",
"telugu": "te",
"english": "en"
}
var lang_var = lang_code[fullData['edition']];
var translate_options = { method: 'POST',
url: 'https://translation.googleapis.com/language/translate/v2',
qs:
{ key: 'Key goes here',
},
form: {
q: fullData.name,
target: lang_var
},
};
request(translate_options, function (error, translate_response, translate_body) {
var farmer_name = "";
if(error){
console.log("There is an error in translation");
console.log(error);
}
translate_body = JSON.parse(translate_body);
if(translate_body.data.translations){
farmer_name = translate_body.data.translations[0].translatedText;
console.log("The farmer name is " + fullData.name +" : " + farmer_name);
// Code to write to the database;
} else{
console.log("The translation failed");
farmer_name = fullData.name;
console.log("The famrer name is " + farmer_name);
}
})
});
You're not returning a promise that's resolved when all the work of your function is complete. If the work was completing in the past, that possibly just means you were lucky. Without returning a promise, Cloud Functions may terminate and clean up any work that wasn't complete when the function returns. Properly returning a promise will prevent Cloud Functions from cleaning up before the work is done.
Please consider reading my blog post about this. There is a section special just for ECONNRESET.