Update Stripe Customer from Firebase Function (Node.js) - node.js

I have the following Firebase Function:
exports.updateStripeCustomer = functions.region('europe-west3')
.firestore
.document('users/{userId}')
.onUpdate( async (change, context) => {
const oldValue = change.before.data();
const newValue = change.after.data();
if( JSON.stringify(newValue.adresse) != JSON.stringify(oldValue.adresse) ) {
const user = await admin.auth().getUser(change.after.id)
.then((user) => {
return user.displayName
})
const stripe = Stripe(<api-key>)
const customer = await stripe.customers.update(oldValue.stripe_id,
{
shipping: {
address: {
city: newValue.ort,
postal_code: newValue.plz,
line1: newValue.strasse,
line2: newValue.hausnum
},
name: user
},
}
);
return customer
}
return 'no Data changed'
})
The function itself works - only the format for passing the shipping information is not clear to me and seems wrong. The example from the Stripe Api documentation is not quite clear to me.
Stripe return the following error:
parameter_unknown - shipping[0]
Received unknown parameter: shipping[0]
Complete LOG Error:
{
"shipping": {
"0": {
"name": "Testi Tester"
}
}
}
-------------
Response-Text
{
"error": {
"code": "parameter_unknown",
"doc_url": "https://stripe.com/docs/error-codes/parameter-unknown",
"message": "Received unknown parameter: shipping[0]",
"param": "shipping[0]",
"type": "invalid_request_error"
}
}
Many Thanks for Help !

Your parameters appear to conform to the API docs for shipping (link). The error structure suggests that your shipping object is being interpreted as an array, which means something isn't being stringified properly for the request.
My hunch that that this is related to your name: user child parameter. This should just be a string, but based on the line user.displayName earlier in your snippet it appears user is a complex object. Did you mean to use the same user.displayName in the update request?
I'd suggest you try putting in a temporary test value name: 'test name' to see if that resolves the issue, then figure out which attribute from your user object you want to use.

Related

I have a Joi schema and I want that when I do a POST method the id is included and required and with PUT method the id is not taken into account

I have a schema joi object:
const commonFields = {
id: joi.string().required(),
name: joi.string().required().min(5).max(50),
nif: joi.string().length(9).required(),
diet: joi.bool().required().strict(),
vegetarian: joi.bool().required().strict(),
};
const aluno = joi.object({
...commonFields,
num: joi
.string()
.required()
.regex(/^\d{1,4}\/\d{2}$/),
regime: joi.string().required().valid("externo", "interno"),
});
(...)
When i make a PUT request to update a user i want to ignore the field "id" but when i make a POST request i want to make it required.
I tried the following:
In schema I added alter() to the field "id":
id: joi.string().alter({
post: (schema) => schema.required(),
put: (schema) => schema.forbidden(),
}),
And in my functions i did this:
async function updateUser(req, res, type, db) {
try {
const { error } = requestValidation[type].validate(req.body, {
context: { method: req.method }
});
if (error) {
return res.status(400).send({ error: error.message });
}
const id = req.params.id;
const { name, nif, vegetarian, diet } = req.body;
(..)
But when i call this function in my PUT endpoint to update a user and add the field id into the requesition body it doesn't throw an error like it should throw. The response should be like this when i add the id to the body:
{
"error": "\"id\" is not allowed"
}
I want to ignore the id because I want to receive it by req.params.id.
I may not be doing the best way but I'm open to new suggestions!
The documentation has an example using tailor with alter
const { error } = requestValidation[type].tailor(req.method.toLowerCase()).validate(req.body, {
context: { method: req.method }
});

MERN - update specific string in an array's object

I am using mongoose to connect my backend (Express) server to database. I want to do normal CRUD operations - but I am able to do it only for direct data in object, but I need to be able to access also array data.
Example of my model:
const LeewaySchema = new mongoose.Schema({
name: {
type: String,
},
shirt: [
{
name: String,
image: String,
},
],
With the following code I am able to update only name of the object, but I need to be able to update also name in shirt array
Here is working approach when changing name of object:
app.put('/update', async (req, res) => {
const updateName = req.body.updateName;
const id = req.body.id;
console.log(updateName, id);
try {
await ClosetModel.findById(id, (error, closetToUpdate) => {
closetToUpdate.name = updateName;
closetToUpdate.save();
});
} catch (err) {
console.log(err);
}
res.send('success');
});
And I tried the same with shirt array, just specifying the correct path
app.put('/update-shirt', async (req, res) => {
const updateShirtName = req.body.updateShirtName;
const id = req.body.id;
try {
await ClosetModel.findById(id, (error, closetToUpdate) => {
closetToUpdate.shirt.name = updateShirtName; // different path here
closetToUpdate.save();
});
} catch (err) {
console.log(err);
}
res.send('success');
});
The server crashes and /update-shirt conflicts with /update path
I am using the same route and frontend for READ
useEffect(() => {
axios
.get('http://localhost:8000/read')
.then((response) => {
setListOfClosets(response.data);
})
.catch(() => {
console.log('error');
});
}, []);
And update name function calling with button onClick:
const updateCloset = (id) => {
const updateName = prompt('Enter new data');
axios
.put('http://localhost:8000/update', {
updateName: updateName,
id: id,
})
.then(() => {
setListOfClosets(
listOfClosets.map((val) => {
return val._id === id
? {
_id: id,
name: updateName,
email: val.email,
}
: val;
})
);
});
};
I don't really know how to do update for shirt's name, I tried to copy paste and just change path and url of course, but it did not work.
The question doesn't actually describe what specific transformation (update) you are attempting to apply to the document. Without knowing what you are attempting to do, there is no way for us to help advise on how to do it.
Say, for example, that the document of interest looks like this:
{
_id: 1,
shirt: [
{ name: "first shirt", image: "path to first shirt" },
{ name: "second shirt", image: "path to second shirt" },
{ name: "third shirt", image: "path to third shirt" }
]
}
Also let's say that the application hits the /update-shirt endpoint with an id of 1 and a updateShirtName of "updated shirt name". Which entry in the array is that string supposed to be applied to? Similarly, how would that information be passed to the server for it to construct the appropriate update.
It is absolutely possible to update documents in an array, here is some documentation about that specifically. But the actual structure of the command depends on the logic that you are attempting to provide from the application itself.
The only other thing that comes to mind here is that the motivation for the schema described in the question seems a little unclear. Why is the shirt field defined as an array here? Perhaps it should instead just be an embedded document. If so then the mechanics of updating the field in the subdocument are more straightforward and none of the aforementioned concerns about updating arrays remain relevant.
just make an update api where you just have to pass the id and and pass the shirt in the findByIdAndUpdate query and hit the postman by passing the below code.
shirt: [
{
name: "jhrh",
image: String,
},
],

How to pass variables to metafieldsSet mutation in Shopify Api Node js Grahql client?

I was trying to use the metafieldsSet mutation to update metafields in Shopify with the following code:
const client = new Shopify.Clients.Graphql(
process.env.SHOP,
process.env.PASSWORD
)
try {
const metafields = await client.query({
data: `mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
userErrors {
field
message
}
metafields {
key
value
}
}
}
`,
query: {
metafields: [
{
key: 'cb_inventory',
namespace: 'my_fields',
ownerId: 'gid://shopify/ProductVariant/40576138313890',
type: 'number_integer',
value: '25',
},
],
},
})
console.log(metafields)
res.status(200).json({ values: metafields })
} catch (error) {
console.log(error)
res.status(500).json(error)
}
However, the above mutation returns the following error:
Expected value to not be null
Variable $metafields of type [MetafieldsSetInput!]! was provided invalid value
I assume the variable metafields failed to pass into the mutation because when I run the exact same mutation in the Shopify Admin API GraphiQL explorer, there was no error
Shopify Admin API GraphiQL explorer mutation result
I have also looked into the github repo of #shopify/shopify-api. In my understanding, variables are added to the query object.
What am I missing?
Thanks,
Howard
Environment: Next js 11.1.2,
Dependencies: #shopify/shopify-api 1.4.1
Turns out the syntax is incorrect. The variables should be placed inside the variables object, while the mutation statement should be placed inside the query object.
The following code works now:
const metafields = await client.query({
data: {
query: `mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
userErrors {
field
message
}
metafields {
key
value
}
}
}`,
variables: {
metafields: [
{
key: 'cb_inventory',
namespace: 'my_fields',
ownerId: 'gid://shopify/ProductVariant/40576138313890',
type: 'number_integer',
value: '25',
},
],
},
},
})
ref: https://github.com/Shopify/shopify-node-api/blob/main/src/clients/graphql/test/graphql_client.test.ts
To use the intended API version, you need to first initialise the context with the following code:
Shopify.Context.initialize({
API_KEY,
API_SECRET_KEY,
SCOPES: ['read_products', 'write_products'],
HOST_NAME: HOST,
API_VERSION: '2021-10',
})

Why it only returns the first id from my mock up database?

In short I am trying to create a simple api that would return the user with the matching id. I use postman to send requests to my localhost created using node.js with express. It works fine when I request the first user but throws in an error when requesting "John". I am coding along a udemy course and can't figure out what the issue is other than the material is outdated. The error is "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client"
users: [
{
id: "123",
name: "Sally",
email: "sally#gmail.com",
password: "bananas",
entries: 0,
joined: new Date(),
},
{
id: "124",
name: "John",
email: "john#gmail.com",
password: "apples",
entries: 0,
joined: new Date(),
},
],
};
app.get("/profile/:id", (req, res) => {
const { id } = req.params;
let found = false;
database.users.forEach((user) => {
if (user.id === id) {
found = true;
return res.json(user);
}
if (!found) {
res.json("User not found");
}
});
});
From the MDN Web Docs:
There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool.
Early termination may be accomplished with:
A simple loop
A for...of
loop
[Array.prototype.every()][every]
[Array.prototype.some()][some]
[Array.prototype.find()][find]
[Array.prototype.findIndex()][findIndex]
This means that your loop will run through all elements and in fact call res.json multiple times resulting in the ERR_HTTP_HEADERS_SENT error . There are many ways to fix this, here's an one example:
app.get("/profile/:id", (req, res) => {
const {id} = req.params;
for (const user of database.users) {
if (user.id === id) {
return res.json(user);
}
}
res.json("User not found");
});

Graphql is returning "Cannot read property 'id' of undefined", [apollo server, node.js]

I am trying to display one course and to update course topic, using graphql and apollo-server.
This is my code:
const { ApolloServer, gql } = require('apollo-server');
var typeDefs=gql`
input CourseInput {
id: Int
title: String
author: String
description: String
topic: String
url: String
}
type Course {
id: Int
title: String
author: String
description:String
topic:String
url: String
}
type Query {
course(id: Int!): Course
courses(topic:String!):[Course]
allcourses:[Course]
hello: String
}
type Mutation {
updateCourseTopic(id: Int !, topic: String !): Course
createCourse(input: CourseInput): [Course]
deleteCourse(id: Int !): [Course]
}
`;
var coursesData = [
{
id: 1,
title: 'The Complete Node.js Developer Course',
author: 'Andrew Mead, Rob Percival',
description: 'Learn Node.js by building real-world applications with Node, Express, MongoDB, Mocha, and more!',
topic: 'Node.js',
url: 'https://codingthesmartway.com/courses/nodejs/'
},
{
id: 2,
title: 'Node.js, Express & MongoDB Dev to Deployment',
author: 'Brad Traversy',
description: 'Learn by example building & deploying real-world Node.js applications from absolute scratch',
topic: 'Node.js',
url: 'https://codingthesmartway.com/courses/nodejs-express-mongodb/'
},
{
id: 3,
title: 'JavaScript: Understanding The Weird Parts',
author: 'Anthony Alicea',
description: 'An advanced JavaScript course for everyone! Scope, closures, prototypes, this, build your own framework, and more.',
topic: 'JavaScript',
url: 'https://codingthesmartway.com/courses/understand-javascript/'
}
]
var getCourses = function(args){
console.log("delila2")
if(args.topic){
console.log("delila2")
var topic=args.topic;
return coursesData.filter(course=>
course.topic===topic
);
}
else return coursesData
}
var resolvers= {
Query:{
course:getcourse,
courses:getCourses,
allcourses:getAllCourses,
hello: ()=>"Delila"
},
Mutation: {
updateCourseTopic:updateCourseTopic,
createCourse:createCourse,
deleteCourse: deleteCourse,
}
};
function getcourse(args){
var id=args.id;
return coursesData.filter(course=>{
return course.id==id
})[0]
}
function getAllCourses(){
console.log("all courses")
return coursesData;
}
function updateCourseTopic (id, topic){
console.log("id je" ,id)
coursesData.map(course=>{
if(course.id===id){
course.topic=topic;
return course
}
});
console.log("svi", coursesData);
return coursesData.filter(course=>course.id===id)[0]
}
function createCourse(input){
var id = 4;
coursesData = [...coursesData, input.input];
console.log("input je" ,input.input)
console.log("coursesdata" ,coursesData)
//coursesData[id] = input;
return coursesData;
}
function deleteCourse(args){
var id=args;
coursesData.splice(id, 1);
return coursesData;
}
// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
const server = new ApolloServer({ typeDefs, resolvers });
// The `listen` method launches a web server.
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
On my localhost server http://localhost:4000 using graphql and this code I am trying to fetch data to get one specific course and to update course topic.
Get single course:
query getSingleCourse($courseID: Int !){
course(id:$courseID){
title
author
description
url
topic
}
}
{
"courseID": 3
}
Update course topic:
mutation updateCourse($id: Int!, $topic: String !) {
updateCourseTopic(id:$id, topic:$topic)
{
title
url
description
author
topic
}
}
{
"id": 1,
"topic": "something new"
}
The problem is with getSingleCourse is that
"errors": [
{
"message": "Cannot read property 'id' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"course"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'id' of undefined",
" at getcourse (/Users/admin/newProject/server4.js:116:17)",
with updateCourse is similar that id and topic are undefined
{ "data": {
"updateCourseTopic": null } }
You can look at image
Anyone help would be appreciated.
Thank you
A resolver can optionally accept four positional arguments: (parent, args, context, info).
In order to work just need to specify first argument like this:
function getcourse(_,args){
var id=args.id;
return coursesData.filter(course=>{
return course.id==id
})[0]
}
function updateCourseTopic (_,args){
coursesData.map(course=>{
if(course.id===args.id){
course.topic=args.topic;
return course
}
});
return coursesData.filter(course=>course.id===args.id)[0]
}
In My case on the input field i had extra , at the end for the JSON which caused issue. simple but took time to identify.
{
"input": {
"title": "Welcome to ReactjS",
"description": "react js is awesome for frontend web development",
"type": "frontend",
}
}
once i removed the , at the end of json problem resolved

Resources