Javascript serialization params with arrays - wordpress

I'm creating an App that feeds with Woo commerce. By now I can show the products list and access to a product , but I'm not able to create an order.
This is the function that creates and POST's the order :
this.order = function(products, address, tax, total){
var dfd = $q.defer();
var clientId = 2;
var items =[];
for(var i = 0 ; i<products.length ; i++){
var item = {product_id: products[i].id,
quantity: products[i].qty,
price: products[i].price,
};
items.push(item);
}
$http({
method: 'POST',
url: appConfig.DOMAIN_URL + '/wp-json/wc/v1/orders' ,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
paramSerializer: '$httpParamSerializerJQLike',
params: {
consumer_key: appConfig.KEY,
consumer_secret: appConfig.SECRET_KEY,
line_items: items,
customer_id: 1,
total: total,
status: 'completed',
shipping: {
first_name: address.full_name,
address_1: address.street,
city: address.city,
postcode: address.postal_code,
state: address.state
},
shipping_lines: [
{
method_id: 'flat_rate',
method_title: 'Flat Rate',
total: tax
}
]
}
})
.then(function(res){
dfd.resolve(res);
}, function(error){
dfd.reject(error);
})
return dfd.promise;
}
Response is :
{
"code": "woocommerce_rest_required_product_reference",
"message": "Product ID or SKU is required",
"data": {
"status": 400
}
}
Params seems to be there when I inspect with browser :
Name Value
consumer_key ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
consumer_secret cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
customer_id 1
line_items[][price] 24490
line_items[][product_id] 27
line_items[][quantity] 1
shipping_lines[][method_id] flat_rate
shipping_lines[][method_title] Flat Rate
shipping_lines[][total] 0
status completed
total 24490
I tried hardcoding and using the same data from Woocommerce documentation but the result is the same.
With some combinations throws "product id required" , "quantity required" or "SKU required".
Tried with POSTMAN and order is created with no products.
Any help is appreciated
UPDATE
I tried commenting this line :
paramSerializer: '$httpParamSerializerJQLike',
And now order is created but with no products data, only "non array" params were assigned, so im almost sure is a formatting problem...
Example object :
var data = {
payment_method: 'bacs',
payment_method_title: 'Direct Bank Transfer',
set_paid: true,
billing: {
first_name: 'John',
last_name: 'Doe',
address_1: '969 Market',
address_2: '',
city: 'San Francisco',
state: 'CA',
postcode: '94103',
country: 'US',
email: 'john.doe#example.com',
phone: '(555) 555-5555'
},
shipping: {
first_name: 'John',
last_name: 'Doe',
address_1: '969 Market',
address_2: '',
city: 'San Francisco',
state: 'CA',
postcode: '94103',
country: 'US'
},
line_items: [
{
product_id: 93,
quantity: 2
},
{
product_id: 22,
variation_id: 23,
quantity: 1
}
],
shipping_lines: [
{
method_id: 'flat_rate',
method_title: 'Flat Rate',
total: 10
}
]
};

This was my workaround :
$http({
method: 'POST',
url: appConfig.DOMAIN_URL + '/wp-json/wc/v1/orders' ,
//headers: {'Content-Type': 'application/x-www-form-urlencoded'},
headers: {'Content-Type': 'application/json'},
//paramSerializer: '$httpParamSerializerJQLike',
params: {
consumer_key: appConfig.KEY,
consumer_secret: appConfig.SECRET_KEY
},
data: {
line_items: items,
customer_id: 1,
status: 'pending',
shipping: {
first_name: address.full_name,
address_1: address.street,
city: address.city,
postcode: address.postal_code,
state: address.state
},
shipping_lines: [
{
method_id: 'flat_rate',
method_title: 'Flat Rate',
total: tax
}
]
}
})
.then(function(res){
dfd.resolve(res);
}, function(error){
dfd.reject(error);
})
return dfd.promise;
}
})
;
Not sure if this is the cleanest way to pass the parameters but I could not understand why paramserializer was not working. I took some code from another sources.
Hope it helps

Related

How to custom GridJs pagination with supabase?

I'm software engineer in Cambodia.
I want to custom Grid.js pagination with supanase, but I faced a problem.
I don't know the solution because it's not in the documentation.
I'm using Nuxt 3
Please tell me how to implement.
The Code is below:
onMounted(() => {
grid.updateConfig({
columns: [
{ name: 'Avatar', id: 'avatar' },
{ name: 'name', id: 'name' },
{ name: 'gender', id: 'gender' },
{ name: 'email', id: 'email' },
{ name: 'phone', id: 'phone' },
{ name: 'address', id: 'address' },
],
pagination: {
enabled: true,
limit: 5,
server: {
url: (prev, page, limit) => `${prev}&limit=${limit}&offset=${page * limit}`
},
summary: true,
},
server: {
keepalive: true,
data: async (opt) => {
console.log(opt)
const { data: customers, error, count } = await supabase
.from('customers')
.select('id, name, gender, email, phone, address, avatar', { count: 'exact' })
.is('deleted_at', null)
.order('created_at', { ascending: false })
return {
data: customers.map((customer) => [
customer.avatar,
customer.name,
customer.gender,
customer.email,
customer.phone,
customer.address,
]),
total: count,
}
},
},
width: '100%',
search: true,
pagination: true,
fixedHeader: true,
className: {
td: 'sr-td-class',
table: 'sr-table',
},
})
grid.render(table.value)
})
I found resolve:
GridJs configuration:
onMounted(() => {
grid.updateConfig({
columns: [
{ name: 'Avatar', id: 'avatar' },
{ name: 'name', id: 'name' },
{ name: 'gender', id: 'gender' },
{ name: 'email', id: 'email' },
{ name: 'phone', id: 'phone' },
{ name: 'address', id: 'address' },
],
pagination: {
enabled: true,
limit: 5,
server: {
url: (prev, page, limit) => `${prev}&limit=${limit}&offset=${page * limit}`
},
summary: true,
},
server: {
keepalive: true,
data: async (opt) => {
console.log(opt)
const { data: customers, error, count } = await supabase
.from('customers')
.select('id, name, gender, email, phone, address, avatar', { count: 'exact' })
.is('deleted_at', null)
.order('created_at', { ascending: false })
return {
data: customers.map((customer) => [
customer.avatar,
customer.name,
customer.gender,
customer.email,
customer.phone,
customer.address,
]),
total: count,
}
},
},
width: '100%',
fixedHeader: true,
className: {
td: 'sr-td-class',
table: 'sr-table',
},
})
grid.render(table.value)
})
Then create server/api directory
after create file customers.ts in server/api/ directory
This is code in customers.ts file
import { serverSupabaseUser, serverSupabaseClient } from '#supabase/server'
export default defineEventHandler(async (event) => {
const user = await serverSupabaseUser(event)
const client = serverSupabaseClient(event)
const query = useQuery(event)
const from = query.page ? parseInt(query.page) * parseInt(query.limit) : 0
const to = query.page ? from + parseInt(query.limit) : query.limit
if (!user) {
throw createError({ statusCode: 401, message: 'Unauthorized' })
}
const { data, error, count } = await client
.from('customers')
.select('id, name, gender, email, phone, address, avatar', {
count: 'exact',
})
.is('deleted_at', null)
.order('created_at', { ascending: false })
.range(from, to)
return { customers: data, count }
})

One single mutation doesn't seem to invalidate cache

my api service
const ordersApi = createApi({
reducerPath: 'ordersApi',
baseQuery: fetchBaseQuery({ baseUrl: ROOT_ENDPOINT }),
tagTypes: ['Orders', 'Order'],
endpoints: (builder) => ({
getOrder: builder.query<TOrder, string>({
query: (id) => `/orders/${id}`,
providesTags: (result) =>
result
? [
{ type: 'Order', id: result.id },
{ type: 'Order', id: 'ORDER' },
]
: [{ type: 'Order', id: 'ORDER' }],
transformResponse: ({ _id, ...order }: TOrderResponse) => order,
}),
// some other queries
...
createOrder: builder.mutation<TOrder, TCreateOrderDto>({
query: (body) => {
return {
url: '/orders/create',
method: 'POST',
body,
};
},
invalidatesTags: (result) =>
result
? [
{ type: 'Order', id: result.id },
{ type: 'Order', id: 'ORDER' },
]
: [
{ type: 'Order', id: 'LIST' },
],
}),
// some other mutations
...
}),
});
createOrder creates a new Order object or modifies existing (by either adding new product to productsInOrder array or increasing amount property of existing array element).
The order object looks like this
{
"status": "NEW",
"vendor": {
"defaultVendor": false,
"companyName": "Mahamba",
"contactPerson": "Bahamab",
"id": "62b95b8e5603c45a7950de22"
},
"productsInOrder": [
{
"quantityToOrder": 12,
"price": 12,
"orderLimit": 12,
"product": "62b95b9c5603c45a7950de26",
"amount": 4
}
],
"orderId": 51,
"total": 48,
"id": "62ba6d60c784c34497b72652"
}
but for some reason when a new order is created neither getOrders nor getOrder endpoints get refetched and I don't understand why..
getOrder's providesTags id is exactly the same as createOrders invalidatesTags id.
Is nested objects that get mutated the problem?
How do I make sure getOrder is invalidated when a new order is created?

sanity-algolia get error when using pt::text

I'm trying to integrate algolia search with sanity CMS using the sanity-algolia library (ref https://www.sanity.io/plugins/sanity-algolia)
But when i try to get the plain text from Portable Text rich text content using the pt::text function.
i get expected '}' following object body , and i dont really know where im missing a bracket.
(another note: since im hosting sanity by using sanity start, I am using nextjs (my frontend) to run the function instead using the /api routes in nextJS) So sanity has a webhook to this route.
details about the error:
details: {
description: "expected '}' following object body",
end: 174,
query: '* [(_id in $created || _id in $updated) && _type in $types] {\n' +
' _id,\n' +
' _type,\n' +
' _rev,\n' +
' _type == "post" => {\n' +
' title,\n' +
' "path": slug.current,\n' +
' "body": pt::text(body)\n' +
' }\n' +
'}',
start: 107,
type: 'queryParseError'
}
this is the serverless function im running:
export default async function handler(req, res) {
if (req.headers["content-type"] !== "application/json") {
res.status(400);
res.json({ message: "Bad request" });
return;
}
const algoliaIndex = algolia.initIndex("dev_kim_miles");
const sanityAlgolia = indexer(
{
post: {
index: algoliaIndex,
projection: `{
title,
"path": slug.current,
"body": pt::text(body)
}`,
},
},
(document) => {
console.log(document);
return document;
},
(document) => {
if (document.hasOwnProperty("isHidden")) {
return !document.isHidden;
}
return true;
}
);
return sanityAlgolia
.webhookSync(sanityClient, req.body)
.then(() => res.status(200).send("ok"));
}
and my post schema from sanity:
export default {
name: 'post',
title: 'Post',
type: 'document',
fields: [
{
name: 'title',
title: 'Title',
type: 'string',
},
{
name: 'slug',
title: 'Slug',
type: 'slug',
options: {
source: 'title',
maxLength: 96,
},
},
{
name: 'author',
title: 'Author',
type: 'reference',
to: {type: 'author'},
},
{
name: 'mainImage',
title: 'Main image',
type: 'image',
options: {
hotspot: true,
},
},
{
name: 'categories',
title: 'Categories',
type: 'array',
of: [{type: 'reference', to: {type: 'category'}}],
},
{
name: 'publishedAt',
title: 'Published at',
type: 'datetime',
},
{
name: 'body',
title: 'Body',
type: 'blockContent',
},
{
name: 'extra',
title: 'extra',
type: 'blockContent',
},
],
preview: {
select: {
title: 'title',
author: 'author.name',
media: 'mainImage',
},
prepare(selection) {
const {author} = selection
return Object.assign({}, selection, {
subtitle: author && `by ${author}`,
})
},
},
}
sanity api v1 doesnt work with functions, I was using
const sanityClient = client({
dataset: "production",
useCdn: true,
projectId: "project_id",
});
which defaults to v1, but in order to use function i added a apiVersion parameter, which made it use later api version:
const sanityClient = client({
dataset: "production",
useCdn: true,
projectId: "9dz8b3g1",
apiVersion: "2021-03-25",
});

Normalize different collections with a pre-flatted array a.k.a. joins

All normalizr examples are focused on nested objects they are expecting an API result that looks like this:
{
id: 1,
title: 'Some Article',
author: {
id: 1,
name: 'Dan'
}
}
But I actually found that the following format where you have to do the collections joins yourself is the one I'm dealing with the most, can I use normalizr to handle this case for me?
Posts [{ _id: 1, name: 'post text 1', userId: 5, locationId: 8}]
Locations [{ _id: 8, name: 'New York'}]
Users [{ _id: 5, name: 'John Doe', country: 'US'}]
expected result:
{
results: [1], // posts ids
entities: {
posts: {
1: { name: 'post text 1', userId: 5, locationId: 8 }
},
users: {
5: { name: 'John Doe', country: 'US' }
},
locations: {
8: { name: 'New York' },
}
}
}
Docs/github issues nothing mentions it as far as I could find

show limit fields on autoForm

I have this schema:
AdsSchema = new SimpleSchema({
title: {
type: String,
label: "Title"
},
_cityId: {
type: String,
label: "City ID"
},
_categoryId: {
type: String,
label: "Category ID"
},
insertedDateTime: {
type: Date,
label: "Inserted Date Time",
autoValue: function() {
if (this.isInsert) {
return new Date();
} else if (this.isUpsert) {
return {$setOnInsert: new Date()};
} else {
this.unset(); // Prevent user from supplying their own value
}
}
},
insertedBy: {
type: String,
label: "Inserted By"
defalutValue: Meteor.user().username
},
price: {
type: Number,
label: "Price"
},
image: {
type: Object,
label: "Image",
optional: true
},
email: {
type: String,
label: "Email"
},
phoneNumber: {
type: String,
label: "Phone Number",
optional: true
},
desc: {
type: String,
label: "Description",
autoform: {
afFieldInput: {
type: "textarea",
rows: 10
}
}
},
quickOrNot: {
type: Boolean,
label: "Quick Or Not",
}
});
I use quickForm for insert to mongoDB, whit following code:
{{> quickForm schema="AdsSchema" collection="Ads" id="insBaseAds" type="insert"}}
and autoForm generate for me a form with all of schema's fields.
but I want only limit fields to show to user on autform, for example these fields:
title, price, image, email, phoneNumber, desc
and I fill some fields my self, for example these fields:
_cityId: "test",
_categoryId: "test",
insertBy: "test"
how can I use quickForm?
You can use afQuickfields
{{>afQuickFields fields="title, price, image, email, phoneNumber, desc"}}
else you can use type 'hidden' in schema
_cityId: {
type: String,
label: "City ID",
autoform:{
type:"hidden"
}
}

Resources