How to pass a const void * to node.js? - javascript

i'm extending the libspotify wrapper of node-libspotify to support album cover images.
so far, i have the following, working c-binding code:
static Handle<Value> Album_Cover(const Arguments& args) {
HandleScope scope;
// test arguments sanity
assert(args.Length() == 2);
assert(args[0]->IsObject()); // sp_session
assert(args[1]->IsObject()); // sp_album
ObjectHandle<sp_session> *session = ObjectHandle<sp_session>::Unwrap(args[0]);
ObjectHandle<sp_album> *album = ObjectHandle<sp_album>::Unwrap(args[1]);
const byte *image_id = sp_album_cover(album->pointer, SP_IMAGE_SIZE_LARGE);
size_t image_size;
const void *image_data;
if(image_id) {
sp_image *image = sp_image_create(session->pointer, image_id);
image_data = sp_image_data(image, &image_size);
sp_image_release(image);
}
return scope.Close(image_data);
}
i struggle on the last line: how can i pass the raw image data over to node.js when running scope.Close(...)?
thanks for any suggestsions.

You should wrap it in a v8::Value as v8::HandleScope::Close expects a handle to one as an argument.
I guess v8::String should do it - v8::String Class Reference
scope.Close(String::New((const char*)image_data, image_size));
A v8::Array might be useful too - it all depends on how you are going to use the returned value afterwards.
I hope this helps.

Related

unknown data type in typescript

I have below code to access secret manager
export interface ConfigData {
dbname:string;
dbuser:string;
}
I hvae json data in secret manager like
{
"dbname" : "dbname",
"dbuser" : "dbuser"
}
function
private static async fetchSecretData(){
let config_key = 'projects/xx/xx/xx/versions/latest';
const client = new SecretManagerServiceClient();
const [version] = await client.accessSecretVersion({name:config_key})
console.log(version.payload?.data )
return version.payload?.data?.toString() as unknown as ConfigData;
}
But I need to cast this as unknown as ConfigData,
I want to cast this ConfigData without making it unknown.
If there is no alternate to do this, then what is the best option to get the keys from this unknown object.
Thanks
Two remarks:
toString() will return a string representation of whatever data is, so casting this to any other type is not going work.
It looks like SecretManagerServiceClient is a part of the Google Cloud Secret Manager API. You don't have to provide your own types for those as the #google-cloud/secret-manager package already comes with its own type definitions.
From from this package, I can tell that the payload is typed like this:
/** Properties of a SecretPayload. */
interface ISecretPayload {
/** SecretPayload data */
data?: (Uint8Array|string|null);
}
To me it's not quite clear what this data is supposed to be. If it's a JSON string then you need to parse it with JSON.parse(version.payload.data) and then append as ConfigData to that, to let TypeScript know the shape of the output. If it's a Uint8Array you need to .toString it first before parsing it with JSON.parse.
So you would get something like:
private static async fetchSecretData() {
let config_key = 'projects/xx/xx/xx/versions/latest';
const client = new SecretManagerServiceClient();
const [version] = await client.accessSecretVersion({name:config_key})
if(version?.payload?.data) {
return JSON.parse(version.payload.data) as ConfigData
} else {
return null;
}
}

How to call karate feature file into karate-config.js file [duplicate]

I have an application that creates a token once by using karate.callSingle() in my karate-config file.
This token however expires after some time, so I might need to recreate it after some tests.
My plan would be to set the time of creation in a variable that can be shared in subsequent iterations of the karate-config file, so that I can recreate the token if the time difference is big enough.
Is there a way in Karate in which I can set a variable in the karate-config that can be shared in subsequent iterations ?
In the end I followed Peter Thomas' advice and used Java by "caching" properties between features. Here's my implementation :
var tokenRefreshTimeInMinutes = 5;
var myToken = {};
var KarateCache = Java.type('KarateCache');
var lastRefreshTime = KarateCache.get('lastRefreshTime');
if (!lastRefreshTime || differenceInMinutes(new Date(lastRefreshTime), new Date()) >= tokenRefreshTimeInMinutes) {
myToken = karate.call('theFileRefreshingTheToken');
KarateCache.add('lastRefreshTime', new Date().toUTCString());
KarateCache.add('myToken', JSON.stringify(myToken));
} else {
myToken = JSON.parse(KarateCache.get('myToken'));
}
with this simple KarateCache Java class
private static final Map<String, String> KARATE_CACHE = new ConcurrentHashMap<>();
public static void add(String key, String value) {
KARATE_CACHE.put(key, value);
}
public static String get(String key) {
return KARATE_CACHE.get(key);
}
Are you storing the result of callSingle() to a variable? Like:
var tokenResult = karate.callSingle('createToken.feature', config);
If you save the expiration time to a variable expirationTime inside createToken.feature, you can access it in karate-config.js as tokenResult.expirationTime.

What is a good practice and how to improve my solution of splitting environments?

I've decided on splitting my environments keeping them in .js files in an environment folder and keep all the sensitive information in .env file (use a third-party module 'DOTENV')
That's what I've come up with but I understand that it's not the best practice and there are a lot of things which should have been implemented in a completely different way but I just lack experience and practice.
At first, I tried to use as more " for loop " as it's possible because as far as I know, it's the fastest way to loop through an object, but in some cases, it was much easier to with "map or filter".
It doesn't look nice to assign data by returning a Promise. Maybe there is a way to get data without a Promise?
I would appreciate any suggestions on how the code can be improved and good practices, your experience.
And I am not sure if I used logging right and error handling. That's a completely new thing for me at the moment, but I used "try catch" to catch them and simply logged them on the console and put into a file.
code:
import { readdirSync } from 'fs';
import path from "path";
import { logger } from '../src/utils/logging';
import { merge } from "lodash";
// FIXME: Function returns a Promise with the data.
// It's not comfortable and seem a bad practice - too much code for a simple task,
// and deal with a promise what may outcome in decreasing perfomance
// ( The simplest code, the fastest code )
export let env = getEnvironment().then(
res => { return res },
err => logger.error(err)
);
// TODO: Rewrite this function into a class Environment to keep it organized and implement ES6 standart
async function getEnvironment() {
const mode = process.env.NODE_ENV || 'development';
const rootPath = process.cwd();
const folder = 'environment';
const loadEnvironments = () => {
// Getting the list of available environments in the "environment" folder,
// at the same time excluding index.js file
const list = readdirSync(path.join(rootPath, folder)).filter(file => !/(?=^(index.js))/i.test(file));
const parameters = {};
// Loading the files found in the folder,
// merging them with the help of a "lodash" library
// just to get one common Object with all possible parameters from all found environments
const loaded = list.map(fileName => {
let name = fileName.split('.')[0];
let loadedFile = require(path.join(rootPath, folder, fileName));
const file = loadedFile[name];
merge(parameters, { ...file });
return loadedFile;
});
// Picking the currect mode out of already loaded ones
const current = { ...loaded.filter(file => file[mode]).map(file => file[mode])[0] };
// Returning an object with all parameters
return {
parameters,
current
}
};
const environments = loadEnvironments();
const environment = {} = looping(environments.parameters, environments.current);
function looping(obj, values) {
const collection = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] !== 'object') {
try {
if (values.hasOwnProperty(key)) {
// By a recursive function run through all parameters,
// transforming the keys to uppercased,
// assigning value to 'obj' (file containing all the parameters)
// from the current mode
collection[key.toUpperCase()] = values[key];
} else {
// if there is no such a key in the current mode,
// 'null' is assigned
collection[key.toUpperCase()] = null;
}
} catch (e) {
logger.error(` Missing parameter "${key.toUpperCase()}" in ${mode} mode!!!`);
}
} else {
// Recursing through the object and the nested objects
collection[key.toUpperCase()] = looping(obj[key], values[key]);
}
}
}
return collection;
}
// When parameters are ready,
// the current mode is assigned
environment["MODE"] = mode;
return environment;
}

TypeError: class.function is not a function. (In 'classname.function(param)')

I'm not an expert to JavaScript, but following the logic of languages like Java and others a the following should work, but does not:
//* #flow */
class Assets {
parts: Array<PartsItem>;
constructor() {
this.parts = [];
}
merge(other: Assets) {
this.parts = this.parts.concat(other.parts);
}
}
class PartsItem {
name: string;
}
function addNewAssets(assets: Assets, newAssets: Assets) {
// the out log verifies that both assets are having all expected values and expected structure
console.log("adding ", newAssets);
console.log("to ", assets);
assets.merge(newAssets);
return assets;
}
function run_example() {
let item1: PartsItem = new PartsItem();
item1.name = "part1";
let item2: PartsItem = new PartsItem();
item2.name = "part2";
let assets = new Assets();
let otherAssets = new Assets();
assets.parts.push(item1);
otherAssets.parts.push(item1);
let mergedAssets = addNewAssets(assets, otherAssets);
console.log(JSON.stringify(mergedAssets));
}
run_example();
when calling addNewAssets I would expect to get merged assets as the result, but the result is:
[12:06:55] W | ReactNativeJS ▶︎ Possible Unhandled Promise Rejection (id: 0):
│ TypeError: assets.merge is not a function. (In 'assets.merge(newAssets)', 'assets.merge' is undefined)
Does it have anything to do with JS "unpredictable" this?
Even when some very smart folks has downgraded that question, it is showcasing a pitfall with JS classes and Flow.
Since the assets has been used after being read back from JSON some class information has get lost, even tho the fields has been typed, Flow is not Java and not hard typed.
I'll need to try this: https://github.com/STRML/json-to-flow
or https://www.npmjs.com/package/flow-validator as described here:
How can I get JSON.parse result covered by the Flow type checker?

Why does electron gives access violation trying to access a new Isolate?

I wrote a Node.js addon to be used in Electron framework
The main entry of the addon calls another C++ library that makes a long running operation so I put callbacks in to have reports of the operation progress.
So the C++ library calls a callback in my addon but the Isolate is null so I tried to create a new one
The isolate is created well but when I try to use it to have new Local i had this error:
Exception thrown at 0x0000000000000000 in electron.exe: 0xC0000005: Access violation executing location 0x0000000000000000.
Here is an excerpt of my code (the progress_cb variable takes value in the main entry of the addon casting the function passed in Javascript call):
Local<Function> progress_cb;
class ArrayBufferAllocator : public ArrayBuffer::Allocator {
public:
virtual void* Allocate(size_t length) {
void* data = AllocateUninitialized(length);
return data == NULL ? data : memset(data, 0, length);
}
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
virtual void Free(void* data, size_t) { free(data); }
};
void progress_callback(int read, int write, int total)
{
V8::Initialize();
ArrayBufferAllocator allocator;
Isolate::CreateParams params;
params.array_buffer_allocator = &allocator;
Isolate* isolate = Isolate::GetCurrent();
if (!isolate) {
isolate = Isolate::New(params);
isolate->Enter();
}
const unsigned argc = 3;
Local<Integer> r = Int32::New(isolate, (int32_t)read);
Local<Integer> w = Int32::New(isolate, (int32_t)write);
Local<Value> t = Int32::New(isolate, (int32_t)total);
Local<Value> argv[argc] = { r, w, t };
progress_cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
}
Any suggestion?

Categories