Calls within callback do nothing - javascript

I am trying to make a call to another function from a callback but it does not do anything even if the callback is executed. What can be?
This is React Native, although in this payment gateway code there is no JSX involved:
var conektaApi = new Conekta();
conektaApi.setPublicKey('key_CWraZrrnBCZ5aFP6FtYNz9w');
conektaApi.createToken({
cardNumber: '4242424242424242',
name: 'Manolo Virolo',
cvc: '111',
expMonth: '11',
expYear: '21',
}, function(data){
this.callAnotherFunction()
//I also tried anonymous function and arrow
// ()=>{ callAnotherFunction}
}, function(){
console.log( 'Error!' );
})
}
In no way am I succeeding in having another function executed in case of success. In github you can find the js code of the Conekta module, which is a very simple code in reality but I can not deduce what is happening or how to fix it.
I think the problem is in the index.js of the dependency of Conekta:
Conekta module js
This contains the index.js of Conekta:
/**
* #providesModule Conekta
*/
'use strict';
// RNConekta
const RNConekta = require('react-native').NativeModules.RNConekta;
const Platform = require('react-native').Platform;
var Conekta = function() {
this.publicKey = false;
};
/**
* Params:
* publicKey: String (Your testing or production Public Key)
*/
Conekta.prototype.setPublicKey = function(publicKey: String) {
this.publicKey = publicKey;
};
/**
* Params:
* info = {
* cardNumber: String
* name: String
* cvc: String
* expMonth: String
* expYear: String
* }
*/
Conekta.prototype.createToken = function(info: Object, success: Function, error:Function) {
info.publicKey = this.publicKey;
RNConekta.createToken(info, function(response){
if ( Platform.OS === 'android' ) {
success( JSON.parse( response ) );
} else {
success( response );
}
}, error);
};
module.exports = Conekta;

I'm not familiar with Conekta but I think you need to specify a parameter for an error too in your function(data) line like this:
function(err, data){
if (err) {
console.error(err);
return;
}
this.callAnotherFunction()
}

Related

How to add Wrap resolver in NestJS and GraphQL to check if email from header is equal to the email in query

I'm using cognito authentication,
I create a middlware
const { email } = payload;
req.headers['user-email'] = email as string;
I want to write this kind of function
public async httpCheck(query: any, args: any, context: any,
resolveInfo: any) {
console.log('authhealth');
console.log("context "+ context.userEmail);
console.log("query : "+ query.userEmail);
(context.userEmail === query.userEmail ) ? console.log("authorized successfully") : console.log("authorization failed");
return 'OK';
}
This is my file structure, I want to write wrap resolver
From your example, it looks like you are wanting to reject the whole request if the email from the request header does not match an email being provided as an argument to a field in the GraphQL query.
So given the following query:
query MyQuery($userEmail:String!) {
userByEmail(email: $userEmail) {
id
email
familyName
givenName
}
}
If you want to check that the header email equals the email argument of userByEmail BEFORE Postgraphile executes the operation, you need to use a Postgraphile Server Plugin which adds a dynamic validation rule that implements the check:
import type { PostGraphilePlugin } from "postgraphile";
import type { ValidationRule } from "graphql";
import { GraphQLError } from "graphql";
import type { IncomingMessage } from "http";
import type { Plugin } from "graphile-build";
// Defines a graphile plugin that uses a field argument build hook to add
// metadata as an extension to the "email" argument of the "userByEmail" field
const AddEmailMatchPlugin: Plugin = (builder) => {
builder.hook(
"GraphQLObjectType:fields:field:args",
(args, build, context) => {
// access whatever data you need from the field context. The scope contains
// basically any information you might desire including the database metadata
// e.g table name, primary key.
const {
scope: { fieldName, isRootQuery },
} = context;
if (!isRootQuery && fieldName !== "userByEmail") {
return args;
}
if (args.email) {
return {
...args,
email: {
...args.email,
// add an extensions object to the email argument
// this will be accessible from the finalized GraphQLSchema object
extensions: {
// include any existing extension data
...args.email.extensions,
// this can be whatetever you want, but it's best to create
// an object using a consistent key for any
// GraphQL fields/types/args that you modify
myApp: {
matchToUserEmail: true,
},
},
},
};
}
return args;
}
);
};
// define the server plugin
const matchRequestorEmailWithEmailArgPlugin: PostGraphilePlugin = {
// this hook enables the addition of dynamic validation rules
// where we can access the underlying http request
"postgraphile:validationRules": (
rules,
context: { req: IncomingMessage; variables?: Record<string, unknown> }
) => {
const {
variables,
// get your custom user context/jwt/headers from the request object
// this example assumes you've done this in some upstream middleware
req: { reqUser },
} = context;
if (!reqUser) {
throw Error("No user found!");
}
const { email, role } = reqUser;
const vr: ValidationRule = (validationContext) => {
return {
Argument: {
// this fires when an argument node has been found in query AST
enter(node, key) {
if (typeof key === "number") {
// get the schema definition of the argument
const argDef = validationContext.getFieldDef()?.args[key];
if (argDef?.extensions?.myApp?.matchToUserEmail) {
// restrict check to a custom role
if (role === "standard") {
const nodeValueKind = node.value.kind;
let emailsMatch = false;
// basic case
if (nodeValueKind === "StringValue") {
if (node.value.value === email) {
emailsMatch = true;
}
}
// must account for the value being provided by a variable
else if (nodeValueKind === "Variable") {
const varName = node.value.name.value;
if (variables && variables[varName] === email) {
emailsMatch = true;
}
}
if (!emailsMatch) {
validationContext.reportError(
new GraphQLError(
`Field "${
validationContext.getFieldDef()?.name
}" argument "${
argDef.name
}" must match your user email.`,
node
)
);
}
}
}
}
},
},
};
};
return [...rules, vr];
},
// This hook appends the AddEmailMatchPlugin graphile plugin that
// this server plugin depends on for its custom extension.
"postgraphile:options": (options) => {
return {
...options,
appendPlugins: [...(options.appendPlugins || []), AddEmailMatchPlugin],
};
},
};
export default matchRequestorEmailWithEmailArgPlugin;
Then you need to register the server plugin in the Postgraphile middleware options:
const pluginHook = makePluginHook([MatchRequestorEmailWithEmailArgPlugin]);
const postGraphileMiddleware = postgraphile(databaseUrl, "my_schema", {
pluginHook,
// ...
});
If you just want to reject the userByEmail field in the query and don't care about rejecting before any resolution of any other parts of the request occur, you can use the makeWrapResolversPlugin to wrap the resolver and do the check there.

Javascript await for response before returning result JS plugin

I've build a JS plugin that I'm loading into my project. I'm using Gulp 4 to compile everything, and I'm struggling with one of my methods. The method accepts a boolean value of true or false, and if the value of true is specified, then the function that the method calls runs an API request which updates my plugin's options.
However, in the project that listens for the response of the method, it doesn't account for the response of the API request.
Plugin:
(function() {
this.MyPlugin = function() {
/*
** Settings
**
** Default settings of the plugin that we can overwrite later
*/
const INBOUND_CONFIG = {
features: {
settingOne: 'setting one',
settingTwo: 'setting two'
}
}
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(INBOUND_CONFIG, arguments[0]);
}
/*
** Compile Detailed Settings
**
** Compile detailed settings
*/
function compactFeatures (config) {
const tlp_aff_id = buildTlpAffID(
config.features.cpm_id,
config.features.sub_id,
config.brand
)
return {
tlp_aff_id: tlp_aff_id
}
}
/*
** Get inbound options
**
** Get inbound options if we're not on the inbound page so that they're,
** accessible throughout the app
*/
function getSettings (prefersDB = false) {
purgeInboundConfig(false)
let inbound
if (localStorage.getItem('example--inbound')) {
inbound = JSON.parse(encodeAndDecode(localStorage.getItem('example--inbound'), 'decode'))
} else {
inbound = JSON.parse(localStorage.getItem('example--inbound'))
}
// prefers db
if (prefersDB) {
fetchFromDB()
return
}
let features = { ...compare(inbound, INBOUND_CONFIG.features), ...compactFeatures(INBOUND_CONFIG) }
INBOUND_CONFIG.features = validateInboundSettings(features)
}
/*
** Comparison
**
** We need to compare the inbound settings parsed against the default,
** settings that we have. If one from the inbound settings matches a,
** default one, we overwrite it and use the value from the inbound
*/
function compare (inbound, defaults) {
let settings = defaults
if (!inbound) return settings
for (const [key, value] of Object.entries(inbound)) {
for (var option in defaults) {
if (defaults.hasOwnProperty(option)) {
if (key == option) {
settings[key] = convertToOriginalType(value)
continue
}
}
}
}
return settings
}
/*
** Find Affiliate from DB
**
** Find an affiliate from the DB and fetch the appropriate data
**
*/
function fetchFromDB () {
const query = JSON.parse(encodeAndDecode(localStorage.getItem('example--query'), 'decode'))
// construct the data that we need
const data = {
affiliate: query.cpm_id ?? '',
query: query ?? []
}
makeInboundHTTPRequest('POST', `https://example.com/api/affiliate/find`, 5000, JSON.stringify(data), function () {
const res = JSON.parse(this.response.data.response)
INBOUND_CONFIG.features = res.options ?? []
// rerun to get settings
getSettings()
})
}
/*
** HTTP requests
**
** This function will handle HTTP requests made by the plugin to and from,
** the processor.
*/
function makeInboundHTTPRequest (
type = 'GET',
url = '',
timeout = 30000,
payload = null,
callback
) {
const xhr = new XMLHttpRequest()
xhr.open(type, url, true)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.timeout = timeout
xhr.ontimeout = () => {
callback.apply({
response: { error: true, message: `Timeout exceeded ${timeout}ms`, data: null }
})
}
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.response != null) {
callback.apply({
response: { error: false, message: 'Success', data: xhr }
})
} else {
callback.apply({
response: { error: false, message: 'No data', data: null }
})
}
}
}
xhr.onerror = () => {
callback.apply({
response: { error: true, message: 'Generic error', data: null }
})
}
if (type === 'POST') {
xhr.send(payload)
return
}
xhr.send()
}
/*
** Get Inbound Options
**
** Get Inbound Config
*/
MyPlugin.prototype.getSettings = function(prefersDB = false) {
getSettings(prefersDB)
return INBOUND_CONFIG.features
}
/*
** Extend Defaults
**
** Utility method to extend defaults with user options
*/
function extendDefaults(source, properties) {
var property;
for (property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
}
/*
** Plugin Loaded
**
** Let the client know that the plugin is working!
*/
window.postMessage('example--inbound-ready', '*')
}());
The above code is incomplete and doesn't contain every function, but purely contains the related one to my question which is:
MyPlugin.prototype.getSettings = function(prefersDB = false) {
getSettings(prefersDB)
return INBOUND_CONFIG.features
}
Now, if I add a setTimeout to this method, then INBOUND_CONFIG.features has the correct returned response when the value of true is passed to the function, otherwise it doesn't
Site
/*
** Define our plugins
**
** Define our plugins that we want to enable
*/
var inbound
const plugins = {
inbound: {
isEnabled: false
}
}
/*
** Enable Plugins
**
** Listen for plugins, if they're included then let's enable them
*/
const eventMethod = window.addEventListener ? 'addEventListener' : 'attachEvent'
const eventer = window[eventMethod]
const messageEvent = eventMethod == 'attachEvent' ? 'onmessage' : 'message'
// Listen to message from child window
eventer(messageEvent, function(e) { // eslint-disable-line prefer-arrow-callback
const str = e.data.toString()
if (str == 'myplugin--inbound-ready') {
// Enable the plugin
plugins.inbound.isEnabled = true
// Load Inbound Plugin
if (plugins.inbound.isEnabled) {
mypluginInbound = new MyPlugin()
inbound = mypluginInbound.getSettings(true) <-- this doesn't contain updated api settings
} else {
inbound = ''
}
}
}, false)
How can I make my getSettings method wait for a response, do I add a set timeout to it, or a promise? I can't use async/await in this project for bigger reasons.

TinyMCE .filter() is not a function on mention (Laravel)

I've been trying to crack my problem for quite some time however no matter what I do I can't figure this out. Currently, following the docs from TinyMCE, this code is provided by them.
/* This represents a database of users on the server */
var userDb = {};
userNames.map(function(fullName) {
var name = fullName.toLowerCase().replace(/ /g, '');
var description = descriptions[Math.floor(descriptions.length * Math.random())];
var image = 'https://s3.amazonaws.com/uifaces/faces/twitter/' + images[Math.floor(images.length * Math.random())] + '/128.jpg';
return {
id: name,
name: name,
fullName: fullName,
description: description,
image: image
};
}).forEach(function(user) {
userDb[user.id] = user;
});
/* This represents getting the complete list of users from the server with only basic details */
var fetchUsers = function() {
return new Promise(function(resolve, _reject) {
/* simulate a server delay */
setTimeout(function() {
var users = Object.keys(userDb).map(function(id) {
return {
id: id,
name: userDb[id].name,
};
});
resolve(users);
}, 500);
});
};
/* This represents requesting all the details of a single user from the server database */
var fetchUser = function(id) {
return new Promise(function(resolve, reject) {
/* simulate a server delay */
setTimeout(function() {
if (Object.prototype.hasOwnProperty.call(userDb, id)) {
resolve(userDb[id]);
}
reject('unknown user id "' + id + '"');
}, 300);
});
};
return {
fetchUsers: fetchUsers,
fetchUser: fetchUser
};
})();
/* These are "local" caches of the data returned from the fake server */
var usersRequest = null;
var userRequest = {};
var mentions_fetch = function(query, success) {
/* Fetch your full user list from somewhere */
if (usersRequest === null) {
usersRequest = fakeServer.fetchUsers();
}
usersRequest.then(function(users) {
/* query.term is the text the user typed after the '#' */
users = users.filter(function(user) {
return user.name.indexOf(query.term.toLowerCase()) !== -1;
});
users = users.slice(0, 10);
/* Where the user object must contain the properties `id` and `name`
but you could additionally include anything else you deem useful. */
success(users);
});
};
When I try to change the fake server to get data from my actual server through an API route, however, I get .filter is not a function error. So I figured I would use the Object. values() method, but that doesn't return anything and the console log shows up empty.
This is my logic in my controller (I'm using Laravel btw)
public function getUsers(Request $request) {
$user = User::all();
return $user;
}
The filter problem happens when I change this line :
if (usersRequest === null) {
usersRequest = fakeServer.fetchUsers();
}
To my API call like this:
if (usersRequest === null) {
usersRequest = fetch('api/users/mention');
}
My API response is as follows:
[{id: 1, name: "John", email: "john#doe.com", email_verified_at: null,…},…]
0: {id: 1, name: "John", email: "john#doe.com", email_verified_at: null,…}
1: {id: 2, name: "Admin", email: "vi#example.com", email_verified_at: "2021-02-07 12:01:18",…}
2: {id: 3, name: "Admin2", email: "di#example", email_verified_at: "2021-02-07 12:01:46",…}
Figured it out! After painstakingly trying and trying, I managed to find a solution.
Wrap the tinymce script in a function, I wrapped mine in a function called function tinyMCE()
before the function, run an ajax api call
var usergetNames = [];
$.ajax({
url: '/api/users/mention',
method: 'get',
dataType: 'json',
contentType: 'application/json',
success: function (data) {
usergetNames = data;
tinyMCE();
},
error: function (ex) {
alert(ex.responseText);
}
});
In tinyMCE, replace var userNames line with this
var userNames = usergetNames;
You can get the rest of the code for tinymce mentions in their official documentation page.

Javascript Callback and Custom Stripe Checkout Handler

Mostly working and passing token and price to my backend made some changes as per answer still can't get the booking_id from the createBookingSuccessful callback to pass along with my token to my backend. Pretty new to javascript and the simple stripe checkout was fine initially but I now need to use the custom method to pass along my booking id from my third party booking callback.
function initalizeWidget(duration, title, price, contractor) {
var widget = new TimekitBooking();
widget.init({
app_key: 'live_widget_key_zr3c9idDjH',
resources: [
'1b6097f9-4806-3dec48c8'
],
callbacks: {
createBookingSuccessful: function(response) {
if (response.data) {
// Update the booking)id here.
var booking_id = response.data.id;
console.log(booking_id);
handler = StripeCheckout.configure(stripeCheckoutConfig);
handler.open({
name: contractor,
description: title,
zipCode: true,
amount: price
});
// ...
}
}
},
});
}
var stripeCheckoutConfig = {
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
locale: 'auto',
key: 'pk_test_O9AlqrUIlJTH2a5V0e',
token: function(token) {
// Get the booking_id;
var booking_id = this.booking;
// Send the charge through
$.post("/subscription/web/payment-method/",
{ token: token.id, price: {{ task_price_cents }}, booking_id: booking_id}, function(data) {
if (data["status"] == "ok") {
window.location = "/some-url/";
} else {
// Deal with error
alert(data["message"]);
}
});
}
};
// Simply pass the config.
var handler = StripeCheckout.configure(stripeCheckoutConfig);
You could use a data object to store all the information about your app.
/**
* Your application's data (everything related to your booking
* system on the client-side)
* It can be accessed by the Objects/Functions in the same scope.
*/
var appData = {
booking_id: null,
handler: null
};
/**
* Widget init
*/
var widget = new TimekitBooking();
widget.init({
// ...
callbacks: {
createBookingSuccessful: function(response) {
if (response.data) {
// Update the booking id here.
appData.booking_id = response.data.id;
// ...
}
}
}
};
// Pass the config.
appData.handler = StripeCheckout.configure({
// ...
token: function(token) {
// Get the booking_id;
var booking_id = appData.booking_id;
// Send the charge through
$.post(
"/subscription/web/payment-method/",
{ token: token.id, price: {{ task_price_cents }}, booking_id: appData.booking_id },
// ...
);
}
});
var handler = appData.handler;

Is it possible to modify defined getter function?

Working on a performance reviewing tool on wechat mini apps platform (javascript + native hybrid based on wechat app), I am trying to inject codes into its prototypes, for example the wx.request function.
This is how you would use a wx.request function:
wx.request({
url: 'test.php',
data: {
x: '' ,
y: ''
},
header: {
'content-type': 'application/json'
},
success: function(res) {
console.log(res.data)
}
})
So in order to know how long the request has taken without manually writing adding all the anchors, I tried to inject code by:
var owxrequest = wx.request
wx.request = function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
This failed and I got an Cannot set property "prop" of #<Object> which has only a getter error.
So I realized the the object must have been defined similar to:
wx = {
request: get function(){
...
}
...
}
So I tried:
var owxrequest = wx.request
Object.defineProperty(wx, 'request', {
get: function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
})
This failed with an error (request: fail parameter error: parameter.url should be String instead of Undefined). Then I tried:
var owxrequest = wx.request
Object.defineProperty(wx, 'request', {
set: function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
})
This wouldn't throw an error but it also has no effect when calling wx.request()...
You can implement this by re-define the getter. The point is: the re-defined getter should return a function object, as wx.request is a function:
Object.defineProperty(wx, 'request', {
get: function() {
return function() {
//...
};
}
});
Why I get the error: request: fail parameter error: parameter.url should be String instead of Undefined?
You are trying to access the arguments of the getter itself (the arguments of function in get: function(){...}). This arguments is an empty object and it can be verified by console.log() statement. As it is empty, arguments.url is undefined, that's why wx complains about the parameter.
Here is an working example:
let wx = {
get request(){
return function() {
console.log(10);
return 88;
};
}
};
let oldF = wx.request;
Object.defineProperty(wx, 'request', {
get: function() {
return function() {
console.log(new Date());
return oldF.apply(wx, arguments);
};
}
});
console.log(wx.request());
The above code would print:
2017-08-28T06:14:15.583Z // timestamp
10
88
You could just shadowing the request function.
Simple example:
Shadowing the getter:
// original getter latest
let base = {
num: 1,
get latest() {
return this.num;
}
}
console.log(base.latest);
// shadowing getter latest
Object.defineProperty(base, 'latest', {
get: function() {
return this.num + 1;
}
});
console.log(base.latest);
Simple shadowing a object property
// your original object
let base = {
request(){
console.log('request original');
}
};
base.request()
base.request = () => {
console.log('request new implementation');
};
// now shadow the original request implementation
base.request()

Categories