woocommerce_order_status_completed has no data - wordpress

I have created a webhook for woocommerce_order_status_completed, this hook is successfully triggered when an order is completed.
The problem is I do not get any data from the order completed other than its ID. the response shows as the following.
{"action":"woocommerce_order_status_completed","arg":1640}

I made a plugin with this code that solved the issue :
function set_resource_for_webhook_payload_by_webhook_id($target_webhook_id,
$desired_resource) {
add_filter('woocommerce_webhook_resource', function($resource, $webhook_id) use
($target_webhook_id, $desired_resource) {
if($webhook_id == $target_webhook_id) {
return $desired_resource;
}
return $resource;
}, 10, 2);
add_filter('woocommerce_valid_webhook_events', function($valid_events) use ($target_webhook_id) {
try {
$topic = wc_get_webhook($target_webhook_id)->get_topic();
list($resource, $event) = explode('.', $topic);
if(!empty($event)) {
$valid_events[] = $event;
}
return $valid_events;
} catch (Exception $e) {
return $valid_events;
}
}, 10);
}
//Replace number (3) bellow with your webhook ID:
add_action('init', function(){
set_resource_for_webhook_payload_by_webhook_id(3, 'order');
});
Hope it helps anyone.

Related

Firebase: is it good enough to check if reference exists before start a transaction

I wonder if it is good enough to test if the reference exists
BEFORE I start a transaction on this reference?
e.g: by using .once('value') and snapshot.exists()
I mean if the check is outside the transaction isn't there a risk another user to delete the reference just after the check and before the transacton executor function?
==== edited to include the minimal complete code =====
here is my data in realtime database:
activeOffers
-LKohyZ58cnzn0vCnt9p
details
direction: "city"
seatsCount: 2
timeToGo: 5
uid: "-ABSIFJ0vCnt9p8387a" ---- offering user
And here is my code flow:
===== index.js =====
entries = require('./entries');
/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
console.log('data: ' + JSON.stringify(data));
return entries.askSeats(data);
});
here is my test data sent by Postman:
{
"data":
{
"uid": "-FGKKSDFGK12387sddd", ---- the requesting/asking user
"id": "-LKpCACQlL25XTWJ0OV_",
"details":
{
"direction": "city",
"seatsCount": 1,
"timeToGo": 5
}
}
}
===== entries.js =======
exports.askSeats = function(data) {
const TAG = '[askSeats]: ';
var entryRef = db.ref('activeOffers/' + data.id);
return globals.exists(entryRef)
.then((found)=>{
if (found) {
return dealSeats(entryRef, data);
} else {
return 'Offer not found [' + data.id + ']';
}
});
}
===== globals.js ======
exports.exists = (ref)=>{
return ref.once('value')
.then((snapshot)=>{
return (snapshot.exists());
});
}
===== entries.js =====
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
throw new Error('You've already made a deal.');
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
} else {
throw new Error('Not enought seats.');
}
}
return entry;
})
.then((success)=>{
return success.snapshot.val();
})
.catch((error)=>{
return Promise.reject(error);
});
}
Btw: is this 'throw new Error(......)' is the correct way to break the transaction ?
========= updated with final source ===
Thanks to Doug Stevenson.
So here is my final source that is working fine. If someone sees a potential problem please let me know. Thanks.
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
var abortReason;
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
abortReason = 'You already made a reservation';
return; // abort transaction
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
// Reservation is made
} else {
abortReason = 'Not enought seats';
return; // abort transaction
}
}
return entry;
})
.then((result)=>{ // resolved
if (!result.committed) { // aborted
return abortReason;
} else {
let value = result.snapshot.val();
if (value) {
return value;
} else {
return 'Offer does not exists';
}
}
})
.catch((reason)=>{ // rejected
return Promise.reject(reason);
});
}
If you read a value before a transaction, then read it again inside the transaction, you have absolutely no guarantee that the second read inside the transaction will yield the same result as the initial read outside before the transaction. It could be modified by the time the transaction is performed.
If you want a truly atomic update, only check value that participate in the transaction within the transaction itself, and make a decision about what to do in the transaction handler.

Catch Duplicate Key Error in Wordpress

I have this code and I want to add duplication error catch in it, how can I?
if (!$row_id) {
// insert a new
if ($wpdb->insert($table_name, $args)) {
return $wpdb->insert_id;
}
} else {
// do update method here
if ($wpdb->update($table_name, $args, array('id' => $row_id))) {
return $row_id;
}
}

How do I dynamically publish collections via a Meteor method?

I dynamically create collections with this method:
createPlist: function(jid) {
try {
Plist[jid] = new Meteor.Collection(pid);
} catch(e) {
console.log("oops, I did it again");
}
Plist[jid].insert({
...,
...,
public:true,
uid:this.userId
});
}
Then I am wanting to publish these selectively, and I am attempting to do it via a method:
getPlist: function(jid,pid) {
// var future = new Future();
try {
Plist[jid] = new Meteor.Collection(pid);
} catch(e) {
console.log("oops, I did it again");
}
Meteor.publish(pid, function() {
console.log(Plist[jid].find({}));
// future["return"](Plist[jid].find({}));
return Plist[jid].find();
});
// return future.wait();
},
This returns 'undefined' to my Template helper, and returns nothing (i.e. waits forever) using Future.
Any user can log in and create a Plist collection, which can be either public or not. A user can also subscribe to any collection where public is true. The variable jid is passed to the method 'getPlist' from the template. It is stored in the user's Session.
Thanks! I hope I have explained it well enough!
And of course the template:
Template.plist.helpers({
getPlist: function() {
Pl = []
jid = Session.get('jid');
//alert(jid);
pid = "pl_"+jid;
// console.log(pid);
Meteor.call('getPlist', jid, pid, function(err,res) {
console.log(res); //returns undefined
try {
Pl[jid] = new Meteor.Collection(pid);
} catch(e) {
console.log(e);
}
Meteor.subscribe(pid);
// return Pl[jid].find({}).fetch();
});
}

iron-router except fails?

So i'm just getting started with iron-router, and I've been building a login system. It works via a .onBeforeAction hook before every route, checking if the user is logged in. However, there are a few routes I want public, so I've added an except option, as per the docs. Except the problem is it doesn't work :( can anybody see why?
Router.route('/new', function () {
name: 'new',
this.render('newComp');
});
Router.route('/c/:_id', {
name: 'compPage',
data: function() { return Comps.findOne(this.params._id); }
});
Router.route('/c/:_id/embed', function () {
name: 'embed',
this.layout('empty'),
this.render('compEmbed', {
data: function () {
return Comps.findOne({_id: this.params._id});
}
});
});
function loginFunction(){
// all properties available in the route function
// are also available here such as this.params
if (!Meteor.user()) {
// if the user is not logged in, render the Login template
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.layout('empty');
this.render('login');
}
} else {
// otherwise don't hold up the rest of hooks or our route/action function
this.next();
}
}
Router.onBeforeAction( loginFunction, {
except: ['embed'] // this aint working
});
The problem seems to be in your route definition, the name param should be in the third param of Router.route(), like this (so your route actually didn't have a name, thus the except:['route.name'] doesn't work):
Router.route('/c/:_id/embed', function () {
this.layout('empty'),
this.render('compEmbed', {
data: function () {
return Comps.findOne({_id: this.params._id});
}
});
}, {
name: 'embed',
});
More info about named routes here: http://eventedmind.github.io/iron-router/#named-routes

Mongoose pre.save() async middleware not working on record creation

I am using keystone#0.2.32. I would like to change the post category to a tree structure. The below code is running well except when I create a category, it goes into a deadlock:
var keystone = require('keystone'),
Types = keystone.Field.Types;
/**
* PostCategory Model
* ==================
*/
var PostCategory = new keystone.List('PostCategory', {
autokey: { from: 'name', path: 'key', unique: true }
});
PostCategory.add({
name: { type: String, required: true },
parent: { type: Types.Relationship, ref: 'PostCategory' },
parentTree: { type: Types.Relationship, ref: 'PostCategory', many: true }
});
PostCategory.relationship({ ref: 'Post', path: 'categories' });
PostCategory.scanTree = function(item, obj, done) {
if(item.parent){
PostCategory.model.find().where('_id', item.parent).exec(function(err, cats) {
if(cats.length){
obj.parentTree.push(cats[0]);
PostCategory.scanTree(cats[0], obj, done);
}
});
}else{
done();
}
}
PostCategory.schema.pre('save', true, function (next, done) { //Parallel middleware, waiting done to be call
if (this.isModified('parent')) {
this.parentTree = [];
if(this.parent != null){
this.parentTree.push(this.parent);
PostCategory.scanTree(this, this, done);
}else
process.nextTick(done);
}else
process.nextTick(done); //here is deadlock.
next();
});
PostCategory.defaultColumns = 'name, parentTree';
PostCategory.register();
Thanks so much.
As I explained on the issue you logged on Keystone here: https://github.com/keystonejs/keystone/issues/759
This appears to be a reproducible bug in mongoose that prevents middleware from resolving when:
Parallel middleware runs that executes a query, followed by
Serial middleware runs that executes a query
Changing Keystone's autokey middleware to run in parallel mode may cause bugs in other use cases, so cannot be done. The answer is to implement your parentTree middleware in serial mode instead of parallel mode.
Also, some other things I noticed:
There is a bug in your middleware, where the first parent is added to the array twice.
The scanTree method would be better implemented as a method on the schama
You can use the findById method for a simpler parent query
The schema method looks like this:
PostCategory.schema.methods.addParents = function(target, done) {
if (this.parent) {
PostCategory.model.findById(this.parent, function(err, parent) {
if (parent) {
target.parentTree.push(parent.id);
parent.addParents(target, done);
}
});
} else {
done();
}
}
And the fixed middleware looks like this:
PostCategory.schema.pre('save', function(done) {
if (this.isModified('parent')) {
this.parentTree = [];
if (this.parent != null) {
PostCategory.scanTree(this, this, done);
} else {
process.nextTick(done);
}
} else {
process.nextTick(done);
}
});
I think it's a bug of keystone.js. I have changed schemaPlugins.js 104 line
from
this.schema.pre('save', function(next) {
to
this.schema.pre('save', true, function(next, done) {
and change from line 124 to the following,
// if has a value and is unmodified or fixed, don't update it
if ((!modified || autokey.fixed) && this.get(autokey.path)) {
process.nextTick(done);
return next();
}
var newKey = utils.slug(values.join(' ')) || this.id;
if (autokey.unique) {
r = getUniqueKey(this, newKey, done);
next();
return r;
} else {
this.set(autokey.path, newKey);
process.nextTick(done);
return next();
}
It works.

Resources