Get fully-qualified path of Protobuf message in JavaScript - javascript

I am trying to get the fully-qualified path of my Protobuf message type in JavaScript. For example, given the following file: status.proto
package my.messages.proto;
message Status {
string code = 1;
}
Once compiled with protoc, I can then do something like:
import { Status } from 'gen/my/messages/proto/status_pb.js';
const status = new Status();
But then how can I get the fully-qualified path of this message? I want a string of my.messages.proto.Status, but I can't seem to find any API where this is possible. I basically want the equivalent of the C++ function message.GetDescriptor()->full_name().
If I do console.log(status);, I see something printed out like:
my.messages.proto.Status {wrappers_: null, messageId_: undefined, arrayIndexOffset_: -1, array: Array(1), pivot_: 1.7976931348623157e+308, …}
So, the information is there, but just not sure how I can access it. Is this possible in JavaScript with Protobuf?

All the proto message types generated using protoc_node are available on the global proto namespace in node as a key:class object. So you can check whether your message is an instance of one of the key's values and return the key:
import { MessageType } from './MessageType_pb'
const message = new MessageType()
export function getSubjectFromMessage(message: Message){
for (const key in proto){
if(message instanceof proto[key]){
return key
}
}
}
getSubjectFromMessage(message) === "MessageType" // true
Nasty that they don't provide a class.name or something.

Related

How to replace the object key name dynamically Angular 8

I want to rename the factORLossTree into savefactORLossTree dynamically inside the function from below payload.
I am getting below data on payload after submitting the form.
{
"cluster":"Europe",
"factory":"Caivano",
"factoryId":"Caivano",
"factORLossTree":[
{
"skuid":"000000000067334539",
"skuDescription":"MAG 55ml Mini PistHazelnut 8MP x6x120 EB",
"levelLosses":[
{
"level1":"Line Scheduling Losses",
"variancePer":100
}
],
"isRowChecked":false
}
],
"submitType":"po"
}
Below is my code .
saveOrUpdateORData() {
const formData = Object.assign({}, this.orLosstreeForm.value);
if (formData.factORLossTree.length === 0) {
this.dialogService.openDialog('Data Not Available');
return false;
}
console.log(formData,"formdata");
return;
}
Expected Output
{
"cluster":"Europe",
"factory":"Caivano",
"factoryId":"Caivano",
"savefactORLossTree":[
{
"skuid":"000000000067334539",
"skuDescription":"MAG 55ml Mini PistHazelnut 8MP x6x120 EB",
"levelLosses":[
{
"level1":"Line Scheduling Losses",
"variancePer":100
}
],
"isRowChecked":false
}
],
"submitType":"po"
}
Can anyone please help me to do this.
Sure thing! Worth mentioning that this issue is purely JS and has nothing to do with your framework (e.g. - Angular)
To the point: let's assume that your payload is stored in a variable called payload
First - check if the property exists. Then - replace it:
const propertyName = 'put the property name here'
const newPropertyName = 'put the new property name here'
if (payload.hasOwnProperty(propertyName)) {
payload[newPropertyName] = payload[propertyName]
delete payload[propertyName]
}
Why this is working? Because we are creating another reference to the original data before deleting the property. At the end we end-up with one reference having the updated name
If (from some reason) you need to clone the data, follow this pattern instead:
if (payload.hasOwnProperty(propertyName)) {
const clone = JSON.parse(JSON.stringify(payload[propertyName]))
payload[newPropertyName] = clone
delete payload[propertyName]
}
I assume that your property always contains an object. If the type of the property is primitive (e.g. - number, string or boolean) you can just skip the cloning phase all together (primitives are copied by their values while only objects are handled by their reference)

How to retrieve contents of message.json file with chrome.i18n web extension API

When using chrome's chrome.i18n API, the chrome.i18n.getMessage method retrieves only one message at a time.
const buttonText = chrome.i18n.getMessage('buttonText');
What I want is to update the popup UI when the popup opens with the localized contents of message.json file. Doing so requires me to know the message names used. Is there a method/technique similar to getMessage for retrieving all the contents of the message.json file at once?
Right now, I can only keep all the message names somewhere and retrieve each localized message individually like:
const messages = ["buttonText", "buttonTitle"];
messages.forEach((message) => {
const messageText = chrome.i18n.getMessage(message);
// Update UI
});
To get the "messages.json" file you would have to use "fetch" and access the URL of the file with the "chrome.runtime" API and the "getURL" method.
fetch(chrome.runtime.getURL(`_locales/en/messages.json`))
The only thing is that in this way you have to specify the language yourself and verify it, that's how I did it:
let lang = 'en';
if (["es"].includes(chrome.i18n.getUILanguage().split('-')[0])) {
lang = chrome.i18n.getUILanguage().split('-')[0];
}
fetch(chrome.runtime.getURL(`_locales/${lang}/popup_content.json`))
Detecting the language of the browser and verifying if it exists in my extension apart from the default language, in this case English, if it does not exist in the array it is not modified and remains by default.
My "_locales" directory:
_locales -> en -> messages.json
_locales -> es -> messages.json
Finally, to iterate the result of "messages.json", as it is an object, I found these ways:
for (let key in obj) {
console.log(obj[key]);
}
Object.keys(obj).forEach(key => {
console.log(obj[key]);
});
Both ways iterate and return the keys of the given object. more info: Working with objects.
Final code: this will return each value of the "message" key of each object within the main object of the json.
let lang = 'en';
if (["es"].includes(chrome.i18n.getUILanguage().split('-')[0])) {
lang = chrome.i18n.getUILanguage().split('-')[0];
}
fetch(chrome.runtime.getURL(`_locales/${lang}/messages.json`)).then(res=> {
res.json().then(json=> {
Object.keys(json).forEach(key => {
console.log(json[key].message);
});
});
});
The file "_locales -> en -> messages.json":
{
"appName": {
"message": "Name of my extension"
},
"appDesc": {
"message": "Awesome extension."
}
}
The result in the console will be something like:
Name of my extension
Awesome extension.
Sorry if the wording is not correct or I am very repetitive, I had to use the translator.

Mongoose: Unable To Save Data To Empty Object

Here is my situation:
I have a simple Schema that has an object called commands which I originally set to {}.
commands:{}
When I went to add a document to the database, I looped through a file of commands and added them to the object by doing command["command"] = "blah blah";. The way I did that is as follows (The code I work is quite old):
Guild.findOne({guildId:serverId}).then(x=>{
x.commands = {};
for(let command of Object.values(commandInfo)){
if(!command.name) continue;
const getSubs = ()=>{
const subArray = {};
if(!command.subCommands) return;
for(let subs of Object.values(command.subCommands)){
subArray[subs.name] = {
access:subs.access,
disabled:subs.disabled
}
}
x.commands[command.name].subCommands = subArray;
}
x.commands[command.name] = {
access:command.access,
bounded:command.bounded,
disabled:command.disabled,
}
getSubs();
}
console.log("Success!");
x.save();
});
The commands contain the following (I'll use two commands for my example):
work:{
access:"All",
bounded:"A Channel",
disabled:false
},
profile:{
access:"All",
bounded:"Another Channel",
disabled:false,
subCommands:{
bio:{
access:"All",
disabled:true
}
register:{
access:"All",
disabled:false
}
}
Fastforwarding to now, I made a config file that is supposed to edit these commands. The code I wrote works perfectly fine; however, Mongoose is unable to save any of the command's changes. Everything else in my Schema works in the config file except anything related to commands.
I know what the problem. Mongoose can't find the command in the Schema and gets confused somehow. And I know I could fix it by adding all of the commands and their properties to the Schema. The only problem with that is it's tedious and is not a clean solution to me adding any new commands. Plus, I have all of the information for the commands in a separate file, so I don't want to rewrite what I already wrote.
My question is: is there a workaround to my problem? I tried setting commands to Schema.Types.Mixed, but that didn't fix it. I also searched all around for something similar but could not find anything.
Here is the full Schema as requested:
const {Schema, model} = require("mongoose");
const settings = require("../utils/settings");
const guildSchema = Schema({
guildId:String,
symbols:{
prefix:{type:String, default:";"},
currency:{type:String, default:"$"}
},
channels:{
casinoChannels:Array,
fineChannels:Array
},
commands:Schema.Types.Mixed,
hierarchy:[{
name:String,
role:{type:String, default:""},
members:Array
}],
users:[{
userId:String,
cash:Number,
bank:Number,
items:[{
name:String,
amount:Number
}],
timers:{
work:String,
crime:String,
hack:String,
steal:String,
daily:String,
weekly:String,
marry:String,
divorce:String,
idea:String,
report:String,
quiz:String
},
fine:{
fineId:String,
description:String,
report:String,
pay:Number
},
used:String
}],
});
guildSchema.virtual("addUser").set(function(id){
const user = {
userId:id,
cash:settings.cash,
bank:settings.bank
};
this.users.push(user);
return user;
});
module.exports = model("Servers", guildSchema);
Use findOneAndUpdateto update the existing document in the collection.
let the object that you want to update in "commands" in mongodb be in x variable.
Guild.findOneAndUpdate({guildId : serverId},{commands : x));
The object which you have stored in x variable will directly be updated/replaced with this new data when a match is found.
UPDATE TO NOT COMPLETELY REPLACE EXISTING OBJECT
let guild = Guild.findOne({guildId : serverId});
if(guild){
// guild.commands here will have the existing object.
// Modify it as required and then use findOneAndUpdate again
}
The solution is quite simple. I took it from this site: https://mongoosejs.com/docs/2.7.x/docs/schematypes.html
To give credit:
Since it is a schema-less type, you can change the value to anything else you like, but Mongoose loses the ability to auto detect/save
those changes. To "tell" Mongoose that the value of a Mixed type has
changed, call the .markModified(path) method of the document passing
the path to the Mixed type you just changed.
So, whenever I update the commands (aka whenever I change the access, bounded, or disabled variables), I would use the mark modified method on the command function. My code is:
guildSchema.virtual("saveCommands").set(function(name){
if(["access", "bounded", "disabled"].includes(name)) this.markModified('commands');
});

How to deal with standard Error object on frontend?

I have a frontend app with many API requests, but it's a pain to work with error responses.
Sometimes I need to go through many nested objects like: error.response.data.email and sometimes it is error.response.data.address.province[0].
I can't predict all of the errors, and writing manual "parsers" looks like a dirty extra solution to me:
const errorsObject = err.response.data
let errors = ''
Object.keys(errorsObject).map(key => {
if (key === 'address') {
Object.keys(errorsObject.address).map(
key => (errors += `${key} : ${errorsObject.address[key]}\n`)
)
} else {
errors += `${key} : ${errorsObject[key]}\n`
}
return undefined
})
yield toast.error(errors)
Also it still doesn't cover everything.
Is there any frontend parsers for that? If not, our backend is Python(Django), maybe there is a package for backend side? Ideally I'd want to see a flat array of objects {title, message}.
Your error objects are wellformed and the base idea of parsing the errors is right from a frontend perspective.
The only issues I see in your errors response is nesting and hydration.
If you want hydrated responses you should provide to your parser the feature of retrieving correctly data and eventually map it to frontend UI.
Usually I feed my forms with an object, usually called errors where I can safely retrieve the errors related to a field by its name.
IMHO you are doing it "right", try to work on object type instead of object specific key (like "address).
This is an example of a error parser I use very often. When a very deep nesting occurs or array parsing is needed, I usually update the parser to reach the error and gain the ability to retrieve it from my UI, for example to show the error under the field, border it in red and so on:
import _ from 'lodash';
export const getErrors = (errors) => {
const validationErrors = _.get(errors, 'data.validation_messages') ? errors.data.validation_messages : {};
// Iterate over object keys
_.forOwn(validationErrors, (value, key) => {
// Simple error: { key: { errorKey: value }} - Nested Error {key: { nestedErrorKey: { errorKey: value }}
const data = validationErrors[key][Object.keys(validationErrors[key])[0]];
// Check that content of key is neither null or object (nested validation)
if (validationErrors[key] !== null && typeof data !== 'object') {
validationErrors[key] = _.values(value)[0];
} else if (validationErrors[key] !== null && typeof data === 'object') { // TODO: Recursive approach ?
// Trasform nested values so obj: { nestedKey: nestedvalue } becomes obj_nestedKey: nestedValue
_.forOwn(validationErrors[key], (nestedValue, nestedKey) => {
validationErrors[`${key}_${nestedKey}`] = _.values(nestedValue)[0];
});
}
});
return validationErrors;
};
export default getErrors;
In my previous project, every error has an error code coming in the response and on the frontend side, every error code gets mapped with error messages, which was very easy to provide multi-locale error messages. You can try that if you have control on APIs and add one more key as error_code.

Parsing JSON into an object with a defined 'schema'

I have a user specified JSON object that I'm attempting to process in the browser.
The problem is that it needs to match an existing object.
They can't accidentally:
forget to include some fields.
typo fields or deliberately add new fields.
Is there a way to handle this?
so basically if I have an object with foo and bar members, I want their defaults if the user's json is just {} ... and if they accidentally send something like {bart: "asdf";} (typo on 'bar') then I want it to generate an exception.
var default_object = { ... };
var allowed_keys = [ "key1", "key2", ... ];
var new_object = default_object.clone();
for (var key in json_object) {
if (json_object.hasOwnProperty(key)) {
if (allowed_keys.indexOf(key) == -1) {
// Report error here
} else {
new_object[key] = json_object[key];
}
}
}
See here for how to write the clone method I used above. If you use jQuery, you could simplify some of this code by using $.extend().

Categories

Resources