I am communicating to a web service using nodejs and node-soap. But i just can't seem to get the syntax right for passing the parameters to the service.
The documentation says i need to send an array with the field uuid and its value.
Here is the Php code i got as an example from the web service owner
$uuid = "xxxx";
$param = array("uuid"=>new SoapVar($uuid,
XSD_STRING,
"string", "http://www.w3.org/2001/XMLSchema")
)
and here is the code i am using in my node server
function getSoapResponse()
{
var soap = require('soap');
var url = 'http://live.pagoagil.net/soapserver?wsdl';
var auth = [{'uuid': 'XXXXXXXXX'}];
soap.createClient(url, function(err, client) {
client.ListaBancosPSE(auth, function(err, result)
{
console.log(result);
console.log(err);
});
});
With this i get bad xml error
var auth = [{'uuid': 'XXXXXXXXX'}];
or
var auth = [["uuid",key1],XSD_STRING,"string","http://www.w3.org/2001/XMLSchema"];
and with this i get the response "the user id is empty" (the uuid)
var auth = {'uuid': 'XXXXXXXXX'};
Any suggestions?
Finally using the content in this answer and modifying the code in the soap-node module i was able to obtain the code i needed.
I needed something like this:
<auth xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">uuid</key>
<value xsi:type="xsd:string">{XXXXXX}</value>
</item>
</auth>
so I used this for creating the parameters:
var arrayToSend=
{auth :
[
{ 'attributes' : {'xsi:type':"ns2:Map"},
'item':
[
{'key' :
{'attributes' :
{ 'xsi:type': 'xsd:string'},
$value: 'uuid'
}
},
{'value' :
{'attributes' :
{ 'xsi:type': 'xsd:string'},
$value: uuid
}
}
]
}
]
};
and sent it like this:
soap.createClient(url, myFunction);
function myFunction(err, client)
{
client.ListaBancosPSE(arrayToSend,function(err, result)
{
console.log('\n' + result);
});
}
Then the tricky part was modyfing the wsd.js so it didn't add a extra tag everytime i used and array. I went to line 1584 and changed the if for this:
if (Array.isArray(obj))
{
var arrayAttr = self.processAttributes(obj[0]),
correctOuterNamespace = parentNamespace || ns; //using the parent namespace if given
parts.push(['<', correctOuterNamespace, name, arrayAttr, xmlnsAttrib, '>'].join(''));
for (var i = 0, item; item = obj[i]; i++)
{
parts.push(self.objectToXML(item, name, namespace, xmlns, false, null, parameterTypeObject, ancXmlns));
}
parts.push(['</', correctOuterNamespace, name, '>'].join(''));
}
basically now it does not push the open and close tag in every iterarion but instead only before and after the whole cycle.
Also i needed to add the definitions for the xlmns of the message. Client.js:186
xml = "<soap:Envelope " +
"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
'xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
'xmlns:ns2="http://xml.apache.org/xml-soap"' +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
Hopefully this could be of help for people using this library and being in this situation.
There is not much I can do for you but here are a few tips to get you started.
Use client.describe() to see how the service expects the arguments.
The service you are trying to reach has the following structure:
{ App_SoapService:
{ App_SoapPort:
{ Autorizar: [Object],
AutorizarAdvance: [Object],
AutorizarIac: [Object],
ListaBancosPSE: [Object],
AutorizarPSE: [Object],
AutorizarTuya: [Object],
AutorizarBotonCredibanco: [Object],
FinalizarPSE: [Object],
FinalizarTuya: [Object],
ConsultarReferencia: [Object] } } }
Taking a closer look to the specific method ListaBancosPSE it provides this info:
{input: { auth: 'soap-enc:Array' },
output: { return: 'soap-enc:Array' }}
I tried with this:
var soap = require('soap');
function getSoapResponse(url, auth) {
soap.createClient(url, function(err, client) {
console.log(client.describe());
console.log(client.describe().App_SoapService.App_SoapPort.ListaBancosPSE);
client.ListaBancosPSE(auth, function(err, result) {
console.log(JSON.stringify(result));
console.log(err);
});
});
}
getSoapResponse('http://live.pagoagil.net/soapserver?wsdl', {'soap-enc:Array' : {'uuid': 'XXXXXXXXX'}});
The response is the same "Negada, Error nombre de usuario vacio, No se pudo autenticar en pagoagil.net.".
The next steps for you would be to determine which is the message the service is expecting.
Could be something like:
<tns:ListaBancosPSE><uuid>XXXXXXXXX</uuid></tns:ListaBancosPSE>
Or
<tns:ListaBancosPSE><soap-enc:Array><uuid>XXXXXXXXX</uuid></soap-enc:Array></tns:ListaBancosPSE>
Once you know that, you just have to add a console.log in the node-soap package you installed, so go to where you have your node_modules installed and open the file
node_modules/soap/lib/client.js
Add a console.log at line 187, right after the message has been set and
console.log("Message! ", message);
This will show the message, that should give you enough information to figure out the format of the arguments.
Already a few years gone by, but I have another suggestion to solve this problem.
If you (like me) don't come along with all the namespace stuff (due to a lack of understanding), you can directly put serialized XML strings into a value like this:
var objToSend = {
someString: 'stringVal',
arrayContent: {
$xml: '<item>val1</item><item>val2</item>'
}
}
Related
I'm doing a basic 'Visitor Book' function.
Users can submit a little form (with three inputs : name, msg and emoji).
I grab it with req.body in my router component. I'm using nodejs, with express and bodyparser.
I only want to store this data in a JSON, don't want any database involved here.
I'm in trouble with the writeFile method, using 'fs' module.
It work but it push the new data outside the single-array of my JSON file.
Do you know if I can push in inside the array ? Like a .push method, but with writeFile/appendFile/wathever that works good with json files.
Here is my code :
app.post (router) :
app.post('/visitorBook', async (req, res) => {
let formData = {
name: req.body.name,
msg: req.body.msg,
emoji: req.body.emoji
}
try {
console.log(req.body)
let data = JSON.stringify(formData, null, 2);
fs.writeFile("./views/scripts/dataVisitorBook.json", data, { { // dataVisitorBook.json is the storage file
flag:'a' // this flag specify 'please append it' over 'please override file'
}
}, (err) => {
console.log('error :', err)
});
res.redirect('/contact')
} catch (error) {
console.error('/visitorBook route error : ', error)
}
})
My JSON :
[
{
"name": "test1",
"msg": "test1",
"emoji": "<i class='fas fa-hippo fa-3x'></i>"
},
{
"name": "test2",
"msg": "test2",
"emoji": "<i class='fas fa-hippo fa-3x'></i>"
}
]
{
"name": "sd",
"msg": "sd",
"emoji": "<i class='fas fa-kiwi-bird fa-3x'></i>"
}
So the last one with "sd" in name and msg is the pushed one. The 2 other are manually written by me, for readFile tests.
I hope I provided all the information needed. Not used to post here...
Thanks you.
If you read from the existing file and parse it with JSON.parse, you will be able to actually use Array.push on it. And then you can write the stringified result back into the file:
fs.readFile("./views/scripts/dataVisitorBook.json", function (err, data) {
if (err) throw err;
let data = JSON.parse(data.toString('utf8'));
data = JSON.stringify(data, null, 2);
fs.writeFile("./views/scripts/dataVisitorBook.json", data, { { // dataVisitorBook.json is the storage file
flag:'a' // this flag specify 'please append it' over 'please override file'
}
}, (err) => {
console.log('error :', err)
});
})
It might not be optimal though as it is likely to take more time as the file grows bigger.
I appreciate your simple try But using some standards can be much better to you
There are some Standard JSON DBs for Node like :
Simple JSON DB
Node JSON DB
Also, you can try SQLite
I also try with a simple JSON file of use as DB. I faced lots of work and I have managed it too. So my advice is to use some standard libraries
Apart from it, you have to get the file data Parse it as JSON (Decoding) make changes and again serialize it and write into a file (Encoding).
[SOLVED]
Thanks to #Divarrek , I've archieved to make it work.
So :
Read the file with fs.readFileSync.
Then, I store this rawdata in a variable, while parsing it to JSON.
THen, I push it in the 'jsonBook' variable which is the json file temporarly made into a simple object-variable.
Then I write in the file with writeFile, passing the data as variable 'parsed', which contain a JSON.stringified version of my 'jsonBook'
app.post("/visitorBook", async (req, res) => {
let formData = {
name: req.body.name,
msg: req.body.msg,
emoji: req.body.emoji,
};
try {
let rawdata = fs.readFileSync("./views/scripts/dataVisitorBook.json");
var jsonBook = JSON.parse(rawdata);
let formDataParsed = JSON.stringify(formData, null, 2);
jsonBook.push(formData);
let parsed = JSON.stringify(jsonBook, null, 2);
fs.writeFile("./views/scripts/dataVisitorBook.json", parsed, (err) => {
if (err) throw err;
console.log("saved");
});
res.redirect("/contact");
} catch (error) {
console.error("/visitorBook route error : ", error);
}
});
I hope I was clear. Maybe I did some explanation error, I'm trying my best.
There is a nice example how Rollup function could be called via MS CRM WebApi here.
But it covers general access to CRM WebApi. Although in most recent versions new JS namespace Xrm.WebApi was introduced. Which provides more straightforward way to access that endpoint.
Method Xrm.WebApi.execute should be able to execute Rollup request, as it is able to execute WhoAmI. But I'm struggling to figure out correct values of parameters to make this execution happen.
Here is my code:
var RollupRequest = function(entityType, id, query) {
this.Target = { entityType: entityType, id: id };
this.RollupType = "Related";
this.Query = {
Query: query
};
};
RollupRequest.prototype.getMetadata = function() {
return {
boundParameter: null,
parameterTypes: {
Target: {
typeName: "Microsoft.Xrm.Sdk.EntityReference",
structuralProperty: 5
},
RollupType: {
typeName: "Microsoft.Dynamics.CRM.RollupType",
structuralProperty: 3
},
Query: {
typeName: "Microsoft.Xrm.Sdk.Query.FetchExpression",
structuralProperty: 5
}
},
operationType: 1, // This is a function. Use '0' for actions and '2' for CRUD
operationName: "Rollup"
};
};
var request = new RollupRequest(
"contact",
"0473FD41-C744-E911-A822-000D3A2AA2C5",
"<fetch><entity name='activitypointer'></entity></fetch>"
);
Xrm.WebApi.execute(request).then(
function(data) {
console.log("Success: ", data);
},
function(error) {
console.log("Failure: ", error);
}
);
The code generates following URL:
/api/data/v9.0/Rollup(Target=#Target,RollupType=#RollupType,Query=#Query)?#Target={"#odata.id":"contacts(0473FD41-C744-E911-A822-000D3A2AA2C5)"}&#RollupType=&#Query={"Query":"<fetch><entity name='activitypointer'></entity></fetch>"}
and the error: "Expression expected at position 0 in ''."
Which, seems to be, indicates that RollupType was not set correctly, because indeed in URL RollupType is missing.
I assume there are more than one potential error, because I'm using FetchXML as query expression. But meanwhile is it possible indicate what should be changed to generate proper URL at least for RollupType property?
I am trying to create one Node js server with http package. I want to receive only POST request which I have already implemented it. The problem which I am facing is that I am not able to parse JSON correctly (I am expecting one JSON to be attached).
I tried using JSON.parse but that doesn't parse whole json content. It leaves some values as [Object] which is wrong. I saw few packages which is JSONStream but I am not sure how to implement in this case.
server.on('request', function(req, res){
if(req.method == 'POST')
{
var jsonString;
req.on('data', function (data) {
jsonString = JSON.parse(data);
});
req.on('end', function () {
serverNext(req, res, jsonString);
});
}
else
{
res.writeHead(405, {'Content-type':'application/json'});
res.write(JSON.stringify({error: "Method not allowed"}, 0, 4));
}
res.end();
});
Request example:
Here d = JSON file content. (I did this in Python to make this example request)
r = requests.post('http://localhost:9001', headers = {'content-type': 'application/json'}, data = json.dumps(d))
Note: I am able to parse JSON correctly but there are some cases when it shows something like this:
{ 'Heading':
{ 'Content':
{ sometext: 'value',
List: [Object], // Wrong
test: [Array] } } } // Wrong
Update:
Inside serverNext() I am getting few values like:
var testReq = Object.keys(jsonData)[0];
var testId = Object.keys(jsonData[testRequest])[0];
var test = jsonData[testRequest][testId]
Further if I keep on extracting values then at some point it encounters [Objects] value and get crashed.
I can reproduce this "problem" with data as { "Foo": {"Bar": {"Some data": [43, 32, 44]} } } -- it returns the following result: { Foo: { Bar: { 'Some data': [Object] } } }.
As OP mentioned in question, the JSON is parsed correctly, the reason why [Object] is displayed in result is: when JavaScript Object is returned to display, it would be converted to String first by toString() automatically, which will make all object (including array) as [Object] in text.
To display the real content, JSON.stringify() need to be invoked. In your case, the code would be:
req.on('end', function () {
serverNext(req, res, JSON.stringify(jsonString));
});
Please note it is better to rename variable jsonString as jsonObject.
I'm trying to add a new object to my mongodb document inside an array.
I have a NodeJS project using MongoDB which has a collection called "Teste" where i'm saving some random data.
Amongst that data is an array called "ArrayTeste". Currently it is only saving multiple strings because I named my inputs the same thing, so it automatically does it for me.
But I don't want to save each element as an individual string, i need to get these informations, group them in an object and then add it to the array.
Here is my code snippet in NodeJS:
ServicosModel.prototype.Teste = function (req, res) {
console.log("Metodo Teste");
var query =
{
$push:
{
ArrayTeste:
{
Dado1: req.body.Dado1,
Dado2: req.body.Dado2
}
}
}
console.log(query)
this._connection.open(function (errConn, mongoClient) {
console.log("Entrou open")
if (errConn) {
res.end("Deu erro" + errConn);
}
mongoClient.collection("teste", function (errColl, collection) {
if (errColl) {
res.end("Deu erro" + errColl);
}
console.log("Entrou collection")
collection.update(query, function (errUpdate, result) {
console.log("Entrou update")
if (errUpdate) {
res.end("Deu erro" + errUpdate);
} else {
res.end("Deu certo " + result);
}
});
});
});
}
And here is the mongoDB document structure:
{
"_id" : ObjectId("595bf19febbf3c14e481bc28"),
"id" : "2",
"Titulo" : "TItulo do negocio",
"ArrayTeste" : [
"dado1",
"dado2"
]
}
The "id" parameter is one created by me to easy the $elemMatch used in previous tests, so I don't have to search for the _id of the document.
When I run the code and insert stuff into the inputs, I am presented with this error:
(node:8712) UnhandledPromiseRejectionWarning: Unhandled promise
rejection (rejection id: 1): MongoError: document must be a valid
JavaScript object
and I have absolutely no idea of what is happening. the application simply freezes. I have searched through the posts and tried some stuff with $set and $addToSet, but the same error persists.
Any help is appreciated, thanks in advance!
To update a document you need two mandatory parameters:
criteria - to select documents to update
update - to modify selected documents
Here is the driver documentation: https://mongodb.github.io/node-mongodb-native/markdown-docs/insert.html#update.
The error says that collection.update expects an object (the second parameter) and it is receiving a function (your callback function).
To get your code working:
var select = {id: '2'}; // Here we are choosing the document
collection.update(select, query, function (errUpdate, result) {
if (errUpdate) {
res.end("Deu erro" + errUpdate);
} else {
res.end("Deu certo " + result);
}
});
I have created a simple GRPC server and client .
What i want to do is to create a custom error in the server and pass it to the client. My code looks as follows:
Server.js
var error = require('error');
var PROTO_PATH = grpc.load(__dirname + '/proto/hello.proto');
var hello_proto = PROTO_PATH.hello;
function sayHello(call, callback) {
try {
var jsErr = new Error('MY_ERROR');
jsErr.newStatus = 401;
jsErr.newMessage = 'custom unAuthorized error';
console.log(Object.getOwnPropertyNames(jsErr));
console.log(jsErr);
callback(jsErr);
} catch(e) {
callback(e);
}
}
function sayHelloAgain(call, callback) {
callback(null, {message: 'Hello Again ' + call.request.name});
}
function main() {
var server = new grpc.Server();
server.addProtoService(hello_proto.Hello.service, {sayHello: sayHello,sayHelloAgain: sayHelloAgain });
server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
server.start();
}
main();
Client.js
var grpc = require('grpc');
var PROTO_PATH = grpc.load(__dirname + '/proto/hello.proto');
var hello_proto = PROTO_PATH.hello;
function main() {
var client = new hello_proto.Hello('localhost:50051',grpc.credentials.createInsecure());
var user;
if (process.argv.length >= 3) {
user = process.argv[2];
} else {
user = 'world';
}
client.sayHello({name: user}, function(err, response) {
console.log(Object.getOwnPropertyNames(err));
console.log(err);
});
}
main();
and my proto file
syntax = "proto3";
package hello;
service Hello {
rpc sayHello(sayHelloRequest) returns (sayHelloResponse) {}
rpc sayHelloAgain(sayHelloRequest) returns (sayHelloResponse) {}
}
message sayHelloRequest {
string name = 1;
}
message sayHelloResponse {
string message = 1;
}
when i run the cient the result from each looks like this
Server .
[ 'stack', 'message', 'newStatus', 'newMessage' ]
{ [Error: MY_ERROR] newStatus: 401, newMessage: 'custom unAutorized error' }
Client .
[ 'stack', 'message', 'code', 'metadata' ]
{ [Error: MY_ERROR] code: 2, metadata: Metadata { _internal_repr: {} } }
So my created custom javascript error's newStatus, newMessage properties have removed and it has converted to GRPC standard error message .
My Questions are
Is it possible to send a custom message to client ?
Can i create a GRPC error , not a javascript error ?
one way to send custom attributes to client is i think is add the custom data to Metadata . but i am also not sure how to do it .
There is a helpful reply to this same question on the gRPC Google Group:
https://groups.google.com/d/msg/grpc-io/X_bUx3T8S7s/x38FU429CgAJ
You can send a custom status message to the client using the Error
object's message property. In your example, that is "MY_ERROR". The
status code should be in the "code" property, just like how you see it
on the client side.
If you want to use the gRPC status structure instead of a JavaScript
error, you can do so by populating the "code" property and the
"message" or "details" property of the object.
If you want to send metadata, you should construct an instance of the
grpc.Metadata class, then add key/value pairs to the resulting object.
Then you can pass it as the third argument of the callback or set the
error's "metadata" property to send it to the client with the error.
Please note that the status codes that gRPC uses are not HTTP status
codes, but gRPC specific codes that are defined in grpc.status. You
should only set the error's code property using those codes. If you
want to send your own codes, use metadata instead.
I'll illustrate what's written above with some examples.
To send a custom message with the error, construct an Error with the message. This sets the message property:
var jsErr = new Error('Unauthorized');
As mentioned above, it's probably not useful to directly set gRPC status codes in your case. But, for reference, the gRPC status code can be set through the error's code property:
jsErr.code = grpc.status.PERMISSION_DENIED;
To send your own error codes, or other information, use metadata:
var metadata = new grpc.Metadata();
metadata.set('key1', 'value2');
metadata.set('key2', 'value2');
jsErr.metadata = metadata;
Now, if the server constructs the error as above and the client outputs the returned error with:
console.log(Object.getOwnPropertyNames(err));
console.log(err);
console.log(err.metadata);
then the client output is:
[ 'stack', 'message', 'code', 'metadata' ]
{ [Error: Unauthorized]
code: 7,
metadata: Metadata { _internal_repr: { key1: [Object], key2: [Object] } } }
Metadata { _internal_repr: { key1: [ 'value2' ], key2: [ 'value2' ] } }
1.Yes 2.Maybe
Avoid sending special objects (like new Error) over the wire. Send simple object with an error property and look for its value on the other end. See http://json.org/ to have an overview of easy transferable data.
inside Server.js try
function sayHello(call, callback) {
try {
var myCustomError = {};
myCustomError.newStatus = 401;
myCustomError.newMessage = 'custom unAuthorized error';
console.log(Object.getOwnPropertyNames(myCustomError ));
console.log(myCustomError);
callback(null, {error: myCustomError, message: ""});
} catch(e) {
callback(e);
}
}
inside the Client.js
client.sayHello({name: user}, function(err, response) {
var myCustomError= response.error;
if (myCustomError) {
console.log(Object.getOwnPropertyNames(myCustomError));
console.log(myCustomError);
}
});
According to definition of ServerErrorResponse, which you can return as the first parameter in your callback, you can create simple function for this (TypeScript).
function grpcError(): ServerErrorResponse {
return {
message: 'Something wrong', // required param
name: 'Error', // required param
code: Status.UNKNOWN, // code: 2
metadata: undefined,
details: undefined,
stack: undefined,
};
};
Status is enum from 0 to 16
export declare enum Status {
OK = 0,
CANCELLED = 1,
UNKNOWN = 2,
INVALID_ARGUMENT = 3,
DEADLINE_EXCEEDED = 4,
NOT_FOUND = 5,
ALREADY_EXISTS = 6,
PERMISSION_DENIED = 7,
RESOURCE_EXHAUSTED = 8,
FAILED_PRECONDITION = 9,
ABORTED = 10,
OUT_OF_RANGE = 11,
UNIMPLEMENTED = 12,
INTERNAL = 13,
UNAVAILABLE = 14,
DATA_LOSS = 15,
UNAUTHENTICATED = 16
}
So the example code for server is
let error = grpcError();
error.message = "YOUR CUSTOM ERROR MESSAGE";
callback(error, null);
And for client
client.sayHello({name: user}, function(err, response) {
console.log(err);
});
Output would be similar to
Error: 2 UNKNOWN: YOUR CUSTOM ERROR MESSAGE
// stacktrace
{
code: 2,
details: 'YOUR CUSTOM ERROR MESSAGE',
metadata: Metadata { internalRepr: Map(0) {}, options: {} }
}