Update existing records into Odoo using Odoo-await api in NodeJS - node.js

I'm Trying to update an Odoo 15 instance with a collection database that is being retrieved from Google Firestore using an API, I succesfully retrieve the collection from Firestore but in what I'm stuck is mapping this information into the Odoo users and update their records (I have identified the fields that are stored in collection and their respective odoo fields).
This is the API
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');
const Firestore = require('#google-cloud/firestore');
const Odoo = require('odoo-await');
// Initialize app
const firebase = require('firebase-admin');
const credentials = require('./pulse-hris-update-dev-607161dcc967.json');
firebase.initializeApp({
credential: firebase.credential.cert(credentials)
});
// GLOBALS
const version = '0.1'
// HELPERS
// __jlog__
// Easier to type than console.log()
function jlog(m){
console.log(`${(new Date).toISOString()}: ` + m);
}
// __fetchProfiles()__
// z: environment object retuined by setUpEnvironment()
// returns: Array containg profile objects from Firestore.
async function fetchProfiles(){
// set up firestore connector
const start = new Date();
jlog(`${start.toISOString()}: Fetching profiles from Firestore...`);
const db = firebase.firestore();
// get all profiles
const profiles = [];
const qsnap = db.collection('profiles');
const docs = await qsnap.get();
if (docs.empty) throw new Error('Nothing retrieved from Firestore.');
docs.forEach(doc=>{
profiles.push(doc.data());
});
const stop = new Date();
jlog(`${stop.toISOString()}: Fetched ${profiles.length} profiles in ${stop-start}ms.`);
return profiles;
}
// __updatePulse()__
// z: environment object retuined by setUpEnvironment()
// profiles: array of objects returned from fetchProfiles()
async function updatePulse(profiles){
jlog('Updating Pulse...');
if (!profiles || profiles.length === 0) throw new Error('Nothing was retrieved from Firestore.');
const odoo = new Odoo({
host: "HOST",
port: PORT,
database: "DATABASE URL",
username: "USER",
password: "PASS"
});
await odoo.connect();
for (const profile of profiles) {
//search function to retrieve mail in odoo server
//if the profile is new use a create method to map and create the data
//parse function and map field in odoo
const updated = await odoo.update('hr.employee', profile.id, json_parse);
}
await odoo.disconnect();
jlog('... done.');
return true;
}
// __main__
const start = new Date;
const args = yargs(hideBin(process.argv)).argv;
fetchProfiles()
.then(profiles =>{
return updatePulse(profiles);
})
console.log(profiles)
.then(()=>{
// tidy up
const stop = new Date();
jlog(`Finished execution at: ${stop.toISOString()}.\nTotal elapsed time is ${stop-start}ms.`);
})
// END
I have an idea of how the process should go but feel like need some pointers in how to start building the fucntion UpdatePulse() that is going to handle the odoo update.
Currently I think the order should be fetch the IDS of the Odoo users using their email.
Map to the odoo fields that comes from the 'profile' array.
Update each module I want the fields updated
Any ideas or commets are appreciated
Regards

Related

node.js - OrbitDB can't append data

I want to use orbit-db to create database using Nodejs but every time I create a database and try to add a new data it deletes the last element and I only can read the new one. I tried different types of databases (docs, Keyvalue, log, feed) but it's the same problem every time.
Here is my code to create a database:
async function createDb() {
// Create IPFS instance
const ipfsOptions = { repo: './ipfs', }
const ipfs = await IPFS.create(ipfsOptions)
// Create OrbitDB instance
const orbitdb = await OrbitDB.createInstance(ipfs)
// Create database instance
const db = await orbitdb.docs('IOFY')
console.log("address", db.address.toString())
const identity = db.identity
console.log("identity ", identity.toJSON())
console.log("put data")
await db.put({ _id: 'ip2', name: 'shamb0t', followers: 600 })
console.log("get Data")
const profile = db.get('')
console.log("profile = ", profile)
console.log("close")
await db.close();
console.log("disconnect")
await orbitdb.disconnect();
}
It creates the database and displays its address. This is the read function, we need to provide the address of the database as an argument:
async function readDb(fullAdress) {
console.log("read function")
// Create IPFS instance
const ipfsOptions = { repo: './ipfs', }
const ipfs = await IPFS.create(ipfsOptions)
// Create OrbitDB instance
const orbitdb = await OrbitDB.createInstance(ipfs)
const db = await orbitdb.open(fullAdress);
console.log("address", db.address.toString())
await db.load()
console.log("get data")
const value = await db.get('')
console.log("value = ", value)
console.log("close")
await db.close();
console.log("disconnect")
await orbitdb.disconnect();
}
It displays only the last element I added to the database, it's like the last element overwrites the past elements. Why does this happen?

Get data from firestore document and use in cloud function

In the user's collection, each user has a document with a customer_id.
I would like to retrieve this customer_id and use it to create a setup intent.
The following code has worked for me in the past. However, all of a sudden it throws the error:
Object is possibly 'undefined'
The error is on the following line under snapshot.data() in this line:
const customerId = snapshot.data().customer_id;
Here is the entire code snippet:
exports.createSetupIntent = functions.https.onCall(async (data, context) => {
const userId = data.userId;
const snapshot = await db
.collection("development")
.doc("development")
.collection("users")
.doc(userId).get();
const customerId = snapshot.data().customer_id;
const setupIntent = await stripe.setupIntents.create({
customer: customerId,
});
const clientSecret = setupIntent.client_secret;
const intentId = setupIntent.id;
return {
clientsecret: clientSecret,
intentId: intentId,
};
});
Any help is appreciated :)
this is because snapshot.data() may return undefined
there are 2 ways to solve this
first is assert as non-null, if you have high confident that the data exist
const customerId = snapshot.data()!.customer_id;
second if check for undefined
const customerId = snapshot.data()?.customer_id;
if(customerId){
// ....
}
I recommend the 2nd method, it is safer
I can see you are using a sub collection order,You need to loop through the snapshot data using the forEach loop.
const customerId = snapshot.data()
customerId.forEach((id)=> {
console.log(id.customer_id)
});
Try this out but.
The document you're trying to load may not exist, in which case calling data() on the snapshot will return null, and thus this line would give an error:
const customerId = snapshot.data().customer_id;
The solution is to check whether the document you loaded exists, and only then force to get the data from it:
if (snapshot.exists()) {
const customerId = snapshot.data()!.customer_id;
...
}
if you want to fetch user data from docId then you can use something like this:
const functions = require("firebase-functions");
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();
db.settings({ timestampsInSnapshots: true });
exports.demoFunction = functions.https.onRequest((request, response) => {
var userId = request.body.userId;
db.collection("user").doc(userId).get().then(snapshot => {
if (snapshot) {
var data = snapshot.data();
// use data to get firestore data
var yourWantedData = data.name;
// use it in your functionality
}
});
});

My onCreate funciton in Functions of firebase is not creating my desired collection in the cloud database

I just typed a code in my index.js file of functions (firebase CLI).According to my code there must be a timeline collection created in cloud database of firebase.Function is healthy and there are no errors it gets deployed and even in the logs everything works fine. But still timeline collection is not created in the cloud databaese when I follow a user in my app.
this is my code:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.onCreateFollower = functions.firestore
.document("/followers/{userId}/userFollowers/{followerId}")
.onCreate(async (snapshot, context) => {
console.log("Follower Created", snapshot.id);
const userId = context.params.userId;
const followerId = context.params.followerId;
// 1) Create followed users posts ref
const followedUserPostsRef = admin
.firestore()
.collection("posts")
.doc(userId)
.collection("userPosts");
// 2) Create following user's timeline ref
const timelinePostsRef = admin
.firestore()
.collection("timeline")
.doc(followerId)
.collection("timelinePosts");
// 3) Get followed users posts
const querySnapshot = await followedUserPostsRef.get();
// 4) Add each user post to following user's timeline
querySnapshot.forEach(doc => {
if (doc.exists) {
const postId = doc.id;
const postData = doc.data();
return timelinePostsRef.doc(postId).set(postData);
}
});
});
Since you want to execute a variable number of asynchronous calls in parallel, you should use Promise.all(), in order to wait that all these different asynchronous calls are completed before indicating to the CF platform that it can cleanup the CF. See https://firebase.google.com/docs/functions/terminate-functions for more details.
exports.onCreateFollower = functions.firestore
.document("/followers/{userId}/userFollowers/{followerId}")
.onCreate(async (snapshot, context) => {
const userId = context.params.userId;
const followerId = context.params.followerId;
// ...
// 3) Get followed users posts
const querySnapshot = await followedUserPostsRef.get();
// 4) Add each user post to following user's timeline
const promises = [];
querySnapshot.forEach(doc => {
//query results contain only existing documents, the exists property will always be true and data() will never return 'undefined'.
const postId = doc.id;
const postData = doc.data();
promises.push(timelinePostsRef.doc(postId).set(postData));
});
return Promise.all(promises);
});

Firestore: scheduled export

I have used the code from the Firebase documentation to schedule a backup of the data in my Firestore project in a bucket every 6 hours. See the link and the code here:
https://firebase.google.com/docs/firestore/solutions/schedule-export
const functions = require('firebase-functions');
const firestore = require('#google-cloud/firestore');
const client = new firestore.v1.FirestoreAdminClient();
// Replace BUCKET_NAME
const bucket = 'gs://BUCKET_NAME';
exports.scheduledFirestoreExport = functions.pubsub
.schedule('every 24 hours')
.onRun((context) => {
const projectId = process.env.GCP_PROJECT || process.env.GCLOUD_PROJECT;
const databaseName =
client.databasePath(projectId, '(default)');
return client.exportDocuments({
name: databaseName,
outputUriPrefix: bucket,
// Leave collectionIds empty to export all collections
// or set to a list of collection IDs to export,
// collectionIds: ['users', 'posts']
collectionIds: []
})
.then(responses => {
const response = responses[0];
console.log(`Operation Name: ${response['name']}`);
})
.catch(err => {
console.error(err);
throw new Error('Export operation failed');
});
});
Everything works well, my data is saved like I want to but nevertheless I am getting an error:
Error serializing return value: TypeError: Converting circular structure to JSON
Can someone tell me what I should change? Would be glad to get a hint.

Promise not returning value on request

I have been trying to get this to work, but am new to NodeJS. I suspect the issue is due to async, but am not familiar with how it works.
The idea behind this code is that it monitors a firebase database change and sends an email to the users. I am getting everything from the change snapshot, and using the values to check another table for user data. The request is not returning before the email gets sent and I am unsure why.
Edit I should specify that the email function sgMail is firing off before I get the results from the requests. I've tried putting a delay, but I am still not getting the result to return in time.
Here's my index.js
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
var requestify = require('requestify');
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database.ref('Order/{orderID}')
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = 'https://shlep-me-f516e.firebaseio.com/User/'+shipperInfo+'.json';
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
});
function getTravelerData() {
return new Promise(resolve => {
requestify.get('https://shlep-me-f516e.firebaseio.com/User/' + travelerInfo + '.json')
.then(function (response) {
resolve(response.getBody())
});
});
}
var TravelD = getTravelerData();
//Send an email
const msg = {
to: 'andrew#shlepme.com',
from: 'support#shlepme.com',
subject: 'New Follower',
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: 'd1ccfeb9-2e2d-4979-a3ca-c53975fe486e',
substitutionWrappers: ['%', '%'],
substitutions: {
'%shipper_name%': "Test",
'traveler_name': TravelD.name
// and other custom properties here
}
};
console.log('Sending email');
console.log(TravelD);
return sgMail.send(msg)
});
Any ideas? I have been trying to figure this out.
It seems that you need to understand about Promises first.
When you start using promises you will need to ALWAYS use them and chain one with the other.
So I would rewrite your code like this: (not tested)
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require("firebase-functions");
var requestify = require("requestify");
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require("#sendgrid/mail");
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database
.ref("Order/{orderID}")
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = "https://shlep-me-f516e.firebaseio.com/User/" + shipperInfo + ".json";
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
var shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
})
.then(function (shipperResult) {
//Send an email
const msg = {
to: "andrew#shlepme.com",
from: "support#shlepme.com",
subject: "New Follower",
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: "d1ccfeb9-2e2d-4979-a3ca-c53975fe486e",
substitutionWrappers: ["%", "%"],
substitutions: {
"%shipper_name%": "Test",
traveler_name: shipperResult.name
// and other custom properties here
}
};
console.log("Sending email");
console.log(shipperResult);
return sgMail.send(msg);
});
});

Resources