HubspotClient - Update contact by email id is not working - javascript

In NodeJS, I'm using "#hubspot/api-client": "^7.1.2".
Created hubspot client using accessToken as follows
const hubSpotClient = new hubspot.Client({ accessToken });
When I try to update the contact using email it's throwing error
Request:
const idProperty = 'email';
const response = await hubSpotClient(store).crm.contacts.basicApi.update(email, idProperty, contact);
Response:
ERROR {
"statusCode": 404,
"body": {
"status": "error",
"message": "Object not found. objectId are usually numeric.",
"correlationId": "71e911d3...",
"context": {
"id": [
"testemail#..."
]
},
"category": "OBJECT_NOT_FOUND"
}
Create contact is working fine with this client but updating by email is not working.
Anything out of place or syntax error in passing the idProperty?

The problem is in your implementation, because it seems like you are not using properly the Hubspot API.
If you check the function signature of the basicApi.update
public async update(contactId: string, simplePublicObjectInput: SimplePublicObjectInput, idProperty?: string, _options?: Configuration): Promise<RequestContext> {
Basically, you need to pass down a contactId, and then a simplePublicObjectInput that is basically an object that represents your update.
Your code should look like this:
import { Client } from "#hubspot/api-client";
const hubspotClient = new Client({ accessToken: YOUR_ACCESS_TOKEN });
const contactID = 1234;
const response = await hubspotClient.crm.contacts.basicApi.update(contactID, {
properties: { email: 'my-new-email#gmail.com' },
})
Keep in mind that Hubspot always tries to follow their same guidelines as their endpoints. If your check the endpoint specification you will see the following:
Think about the Hubspot node client as just an abstraction of some http client, but at the end does exactly the same as the endpoints described in their implementations.
For that reason, in your implementation, Hubspot is returning an appropriated error, since you are not giving the contactId in the first argument, Hubspot is telling you: "Object not found. objectId are usually numeric." Because indeed a Contact ID is numeric and you are using the value of an email --string-- instead.
If you want to "update by email"
I think that there is no direct way to do it, you might need to do a previous search of the contact by email.
You could use the searchApi.
And after getting the id just run the update.
const searchResponse = await hubspotClient.crm.contacts.searchApi.doSearch({
filterGroups: [
{
filters: [
{
value: 'email-to-search#gmail.com',
propertyName: 'email',
operator: 'EQ',
},
],
},
],
sorts: [],
properties: [],
limit: 1,
after: 0,
});
// Off course you need to improve a lot the error handling here and so on.
// This is just an example
const [contactID] = searchResponse.results;
const contactUpdateResponse = await hubspotClient.crm.contacts.basicApi.update(contactID, {
properties: { email: 'my-new-email#gmail.com' },
})
I hope this helps you!

You CAN use email as the idProperty for the hubspot/api-client get contact function, but it only works if you fill in all the other query fields before idProperty, even if they are undefined.
Here is my example of a getContactByEmail as a Google Cloud Function in Node, using the api-client, and it works great!
exports.getContactByEmail = functions.https.onCall(async (data, context) => {
const email = data.email;
const contactId = email;
const properties = ["firstname", "lastname", "company"];
const propertiesWithHistory = undefined;
const associations = undefined;
const archived = false;
const idProperty = "email";
try {
const apiResponse = await hubspotClient.crm.contacts.basicApi.getById(
contactId,
properties,
propertiesWithHistory,
associations,
archived,
idProperty
);
console.log(JSON.stringify(apiResponse.body, null, 2));
return apiResponse.properties;
} catch (error) {
error.message === "HTTP request failed"
? console.error(JSON.stringify(error.response, null, 2))
: console.error(error);
return error;
}
});

Related

EasyPaisa MA Transaction Error: Incomplete merchant information

I am trying to initiate an MA transaction using easy paisa api. The response error I get after executing the following code is as follows.
'RESPOSNE: '{"orderId": "XYZ123", "responseCode": "0017", "responseDesc": "Incomplete merchant information", "storeId": "xxxxx"}
The response code "0017" is not even mentioned in the documentation.
This is my code
const otcTransaction = async () => {
try {
let url = `https://easypaystg.easypaisa.com.pk/easypay-service/rest/v4/initiate-ma-transaction`
let body = {
emailAddress: 'example#gmail.com',
mobileAccountNo: '03xxxxxxxxx',
orderId: 'XYZ123',
storeId: XXXXX,
transactionAmount: 10.0,
transactionType: 'MA',
}
const response = await axios.post(
url,
body,
{
headers: {
'Credentials': 'base64(username:password)'
},
})
console.log('RESPOSNE: ', response.data)
} catch (error) {
console.log(error.message)
}
}
Let me know if anyone has worked on EasyPaisa MA Transaction
Thanks in advance!
Ask for store Id from EasyPaisa,
I have checked its working fine with proper payload
I got in contact with the easypaisa team and i found out that the code i have written is fine. What needed to be done was that since i was in development and using development credentials i could only use the test phone number provided to me by easypaisa.
(PS keep in mind in test mode we can only make a transaction of Rs 10.0)

Create custom query in Strapi with Mongoose

I'm new to both Strapi and Mongoose, so I apologise if this is a stupid question.
Following the docs (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html) I'm trying to create a custom query in Strapi in which I want to return the whole collection called people sorted by name desc. But when I hit the endpoint I get a 500 error and checking the terminal the error message is CastError: Cast to ObjectId failed for value "alldesc" at path "_id" for model "people".
Here's my code:
services/people.js
module.exports = {
findByNameDesc() {
const result = strapi
.query("people")
.model.find()
.sort({ name: "descending" });
return result.map((entry) => entry.toObject());
},
};
controllers/people.js
module.exports = {
async alldesc(ctx) {
const entities = await strapi.services.people.findByNameDesc(ctx);
return entities.map((entity) =>
sanitizeEntity(entity, { model: strapi.models.people })
);
},
};
config/routes.json
{
"routes": [
...
{
"method": "GET",
"path": "/people/alldesc",
"handler": "people.alldesc",
"config": {
"policies": []
}
}
]
}
What am I doing wrong?
UPDATE: even when removing .sort({ name: "descending" }); from the query, the error is still there, so I'm thinking that maybe there's something wrong in the way I use the service in the controller?
The problem was in routes.json. Basically seems like Strapi doesn't like the slash / so instead of /people/alldesc I tried /people-alldesc and it worked.
Also in the service there's no need for return result.map((entry) => entry.toObject());, that causes anther error, simply doing return result works.

Folding JSON strings to send in header variables - Mailgun

I'm trying to use the JS Mailgun API to send emails. Have it working fine, until I throw template variables into 'h:X-Mailgun-Variables' like so, where jsonString is very large (17000+ characters):
const mailData = {
from: 'Insights <insights#hello.net>',
to: mailAddress,
subject: `Insights: ${DAYS_OF_WEEK[date.getDay()]}, ${MONTHS[date.getMonth()]} ${ordinal_suffix_of(date.getDate())} ${date.getFullYear()}`,
template: "template1",
'h:X-Mailgun-Variables': jsonString,
};
Looking at the documentation here states the following:
Note The value of the “X-Mailgun-Variables” header must be valid JSON string,
otherwise Mailgun won’t be able to parse it. If your X-Mailgun-Variables
header exceeds 998 characters, you should use folding to spread the variables
over multiple lines.
Referenced this post, which suggested I "fold" up the JSON by inserting CRLF characters at regular intervals. This led me here, which still does not work, though logging this does show regular line breaks and is compliant JSON:
const jsonString = JSON.stringify(templateVars).split('},').join('},\r \n');
Any insight into how to properly "fold" my JSON so I can use large template variables in my MailGun emails?
UPDATE:
As requested, adding my code. This works when data only has a few companies/posts, but when I have many companies each with many posts, I get the 400 error:
function dispatchEmails(data) {
const DOMAIN = 'test.net';
const mg = mailgun({apiKey: API_KEY, domain: DOMAIN});
const templateVars = {
date: theDate,
previewText: 'preview',
subject: 'subject',
subhead: 'subhead',
companies: data.companies.map(company => {
return {
url: company.url,
totalParts: data.totalParts,
currentPart: data.currentPart,
companyData: {
name: company.name,
website: company.website,
description: company.description
},
posts: _.map(company.news, item => {
return {
category: item.category,
date: new Date(item.date),
url: item.sourceUrl,
title: item.title,
source: item.publisherName,
description: item.description,
}
})
}
})
};
const jsonString = JSON.stringify(templateVars).split('},').join('},\r \n');
const mailData = {
from: 'test#test.com',
to: 'recipient#test.com',
subject: 'subject',
template: 'template',
'h:X-Mailgun-Variables': jsonString
};
return mg.messages().send(mailData)
.then(body => {
return body;
})
.catch(err => {
return {error: err};
});
}
I think your problem may be overall payload size rather than the string folding. The folding for strings exceeding 998 characters seems to be handled by the node.js client, possibly by the form-data package.
I ran the following test:
test_big_data (template created in mailgun through the UI)
<!DOCTYPE html>
<html>
<body>
<h2>An HTML template for testing large data sets.</h2>
<ul>
<li>{{param_name_0}}</li>
... other lis ...
<li>{{param_name_999}}</li>
</ul>
</body>
</html>
send_email.js
const API_KEY = 'MY_KEY';
const DOMAIN = 'MY_DOMAIN.COM';
const formData = require('form-data');
const Mailgun = require('mailgun.js');
const mailgun = new Mailgun(formData);
const client = mailgun.client({ username: 'api', key: API_KEY });
const bigData = {};
for (let i = 0; i < 400; i++) {
bigData[`param_name_${i}`] = `param_value_${i}`;
}
const dataString = JSON.stringify(bigData);
console.log(dataString.length);
const messageData = {
from: 'Mailgun Sandbox <postmaster#DOMAIN>',
to: 'test#MY_DOMAIN.COM',
subject: 'Big Data Test',
template: 'test_big_data',
'h:X-Mailgun-Variables': dataString,
};
client.messages
.create(DOMAIN, messageData)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.error(err);
});
The length of the dataString in this case is 13781 characters and the email queues successfully.
However, if I bump the for loop condition to i < 1000 I get the following error when queueing the email:
[Error: Bad Request] {
status: 400,
details: '{"message":"Send options (parameters starting with o:, h:, or v:) are limited to 16 kB total"}\n'
}
When I asked Mailgun support about the folding warning form the documentation they pointed me to RFC 2822 section "3.2.3. Folding white space and comments". But like I said, I don't think folding is the issue here.
Cheers!
https://datatracker.ietf.org/doc/html/rfc2822#page-11
Just thinking outside the box but why pass that much data in an email header? I assume you have something on the receiving end which is going to parse the email headers. What if instead of sending them that data you send them a key that they can call back into an API on your end to get the data

Feathers-mongoose : Get by custom attribute in feathers-mongoose

I have a very basic feathers service which stores data in mongoose using the feathers-mongoose package. The issue is with the get functionality. My model is as follows:
module.exports = function (app) {
const mongooseClient = app.get('mongooseClient');
const { Schema } = mongooseClient;
const messages = new Schema({
message: { type: String, required: true }
}, {
timestamps: true
});
return mongooseClient.model('messages', messages);
};
When the a user runs a GET command :
curl http://localhost:3030/messages/test
I have the following requirements
This essentially tries to convert test to ObjectID. What i would
like it to do is to run a query against the message attribute
{message : "test"} , i am not sure how i can achieve this. There is
not enough documentation for to understand to write or change this
in the hooks. Can some one please help
I want to return a custom error code (http) when a row is not found or does not match some of my criterias. How can i achive this?
Thanks
In a Feathers before hook you can set context.result in which case the original database call will be skipped. So the flow is
In a before get hook, try to find the message by name
If it exists set context.result to what was found
Otherwise do nothing which will return the original get by id
This is how it looks:
async context => {
const messages = context.service.find({
...context.params,
query: {
$limit: 1,
name: context.id
}
});
if (messages.total > 0) {
context.result = messages.data[0];
}
return context;
}
How to create custom errors and set the error code is documented in the Errors API.

How to know which params is missing in a query?

I'm setting up a Booking router in NodeJS, and I have many params in.
Now when I forgot params I return an error like :
500: Need more information
I wonder if it's possible to know which params are missing when I return the error code.
This is for a new API made in NodeJS
Here are the params that I want to retrieve from the front ( made in ReactJS )
let body = {
agentDutyCode: "STRING",
RatePlanCode: params.rateCode,
RoomCode: params.roomCode,
AmountAfterTax: params.amountTax,
Start: params.fromDate,
End: params.toDate,
CardCode: params.cardCode,
CardNumber: params.cardNumber,
ExpireDate: params.expireDate,
SeriesCode: params.cvv,
CardHolderName: params.nameCard,
ChainCode: params.chainCode,
HotelCode: params.hotelCode,
RoomQuantities: params.roomQuantities,
GuestQuantitie: params.numberGuest,
GuestPerRoom: params.guestPerRoom,
LastName: params.lastName,
FirstName: params.firstName,
PhoneNumber: params.phoneNumber,
email: params.email,
FVL_SUBUNIT_7: params.walletAddress
}
And this is my promise :
cdsJson.bookResource(req.body)
.then((response) => {
if (response !== null) {
res.response = {
...response
}
} if (response.hotel.length === 0) {
res.respStatus = 500
res.response = {
sendMsg: "Need more informations"
}
next('route')
}
return response
})
If the request succeeds I got a reservation ID otherwise I got :
Error 500: Need more information
Read the documentation or the source code.
Seriously. If the API response doesn't tell you in the error message, then there is no way to know what parameters it expects programmatically.
try it for a for ... in loop like this:
cdsJson.bookResource(req.body)
.then((response) => {
if (response !== null) {
res.response = {
...response
}
} if (response.hotel.length === 0) {
res.respStatus = 500
let errorStr = "Need more informations"
for(var key in req.body) { // Get all parameters that are not set
if(objects[key] == undefined)
errorStr += "\nParameter ["+key+"] is missing!"
}
res.response = {
sendMsg: errorStr
}
next('route')
}
return response
})
You're trying to do server side validation. In Node a good approach would be to define a JSON Schema for the expected parameters and then in your route handler validate the data sent in the request with a JSON Schema validator. This would help you work out whether a request was valid and help you generate error messages automatically. As a rule it's much better (i.e. simpler and more maintainable) to use tools that enable you to declaratively declare your validation (via a schema) than imperatively write code to manually validate objects.
JSON Schema spec https://json-schema.org/
A validator https://github.com/epoberezkin/ajv

Categories