Clockify - Add user is throwing Bad Request 400 error - clockify

I am trying to add a user to my workspace in Clockify however I am getting Bad Request Error: status Code 400.
Can someone help. The API docs are not very helpful. Not sure what is wrong, is it my payload is wrong or am I missing anything in headers?
Thanks for helping. Code below:
const addUser = async (req, res) => {
const url = `https://api.clockify.me/api/v1/workspaces/${workspaceId}`;
try {
const payload = JSON.stringify(req.body);
console.log("DATA", payload); // prints {"email": "max#gmail.com"}
const records = await axios.post(`${url}/users/`, payload, {
headers: {
"X-Api-Key": key,
"content-type": "application/json",
},
});
console.log("Response", records);
res.status(200).json(records);
} catch (error) {
res.statusCode = error.response.status;
res.json({
msg: `Something went wrong. Error: ${error.response.status} with clockify data posting`,
});
console.log(
`Something went wrong :confused:!! Error with clockify data posting`,
error.message
);
}
};

Adding User Via API to clockify is only possible when your workspace is on a paid subscription

Related

How to fix a Firebase Cloud Functions Bad Request in React-Native?

I'm fairly new to Firebase cloud functions and I've been trying to integrate Stripe into my RN project using FB but keep getting a bad request response. I'll see a few things when I try to run this:
Request is missing body.
Invalid request, unable to process.
Bad Request (400).
Does anyone know where I should go from here?
In my index.js file:
exports.payWithStripe = functions.https.onCall(async (data, response) => {
try {
const paymentIntent = await stripe.paymentIntents.create({
data: {
amount: 100,
currency: "usd",
payment_method_types: ["card"]
}
});
const clientSecret = paymentIntent.client_secret;
response.json({
clientSecret: clientSecret,
})
} catch (e) {
console.log(e.message);
response.json({ error: e.message });
}
})
On the app side:
const fetchPaymentIntentClientSecret = async () => {
const response = await fetch(`${FUNCTION_URL}/create-payment-intent`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const { clientSecret, error } = await response.json();
return { clientSecret, error };
}
Your Cloud Function is a Callable Cloud Function, as we can see from the code: functions.https.onCall(). As explained in the doc "HTTPS callable functions are similar but not identical to HTTP functions"
You should call it from your app by using the specific methods of the JS SDK, as explained here.
It is possible to call it via fetch (or any other library used to make XMLHttpRequests from the browser) but then you need to follow the Protocol specification for https.onCall which is not straightforward. So it is highly recommended to use the SDK.

Vue Axios Interceptor Response Firebase 401 Token Expired/Refresh (undefined)

I'm using the following interceptors in a Vuejs v2 website to push a firebase token to my node backend. There in the backend, I detect/verify the token, pull some data using the uid from a database and then process any api calls.
Even though I am using the firebase onIdTokenChanged to automatically retrieve new ID tokens, sometimes, if the user is logged in, yet inactive for an hour, the token expires without refreshing. Now, this isn't a huge deal - I could check in the axios response interceptor and push them to a login page, but that seems annoying, if I can detect a 401 token expired, resend the axios call and have a refreshed token, the user won't even know it happened if they happen to interact with a component that requires data from an API call. So here is what I have:
main.js
Vue.prototype.$axios.interceptors.request.use(function (config) {
const token = store.getters.getSessionToken;
config.headers.Authorization = `Bearer ${token}`;
return config;
});
Vue.prototype.$axios.interceptors.response.use((response) => {
return response }, async function (error) {
let originalRequest = error.config
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
let user = auth.currentUser;
await store.dispatch("setUser", {user: user, refresh: true}).then(() => {
const token = store.getters.getSessionToken;
Vue.prototype.$axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
return Vue.prototype.$axios.request(originalRequest);
});
}
return Promise.reject(error); });
let app;
auth.onAuthStateChanged(async user => {
await store.dispatch("setUser", {user: user, refresh: false}).then(() => {
if (!app) {
app = new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount('#app')
}
})
.catch(error => {
console.log(error);
});
});
vuex
setUser({dispatch, commit}, {user, refresh}) {
return new Promise((resolve) => {
if(user)
{
user.getIdToken(refresh).then(token => {
commit('SET_SESSION_TOKEN', token);
this._vm.$axios.get('/api/user/session').then((response) => {
if(response.status === 200) {
commit('SET_SESSION_USER', response.data);
resolve(response);
}
})
.catch(error => {
dispatch('logout');
dispatch('setSnackbar', {
color: "error",
timeout: 4000,
text: 'Server unavailable: '+error
});
resolve();
});
})
.catch(error => {
dispatch('logout');
dispatch('setSnackbar', {
color: "error",
timeout: 4000,
text: 'Unable to verify auth token.'+error
});
resolve();
});
}
else
{
console.log('running logout');
commit('SET_SESSION_USER', null);
commit('SET_SESSION_TOKEN', null);
resolve();
}
})
},
I am setting the token in vuex and then using it in the interceptors for all API calls. So the issue I am seeing with this code is, I'm making an API call with an expired token to the backend. This returns a 401 and the axios response interceptor picks it up and goes through the process of refreshing the firebase token. This then makes a new API call with the same config as the original to the backend with the updated token and returns it to the original API call (below).
This all seems to work, and I can see in dev tools/network, the response from the API call is sending back the correct data. However, it seems to be falling into the catch of the following api call/code. I get an "undefined" when trying to load the form field with response.data.server, for example. This page loads everything normally if I refresh the page (again, as it should with the normal token/loading process), so I know there aren't loading issues.
vue component (loads smtp settings into the page)
getSMTPSettings: async function() {
await this.$axios.get('/api/smtp')
.then((response) => {
this.form.server = response.data.server;
this.form.port = response.data.port;
this.form.authemail = response.data.authemail;
this.form.authpassword = response.data.authpassword;
this.form.sendemail = response.data.sendemail;
this.form.testemail = response.data.testemail;
this.form.protocol = response.data.protocol;
})
.catch(error => {
console.log(error);
});
},
I have been looking at this for a few days and I can't figure out why it won't load it. The data seems to be there. Is the timing of what I'm doing causing me issues? It doesn't appear to be a CORS problem, I am not getting any errors there.
Your main issue is mixing async / await with .then(). Your response interceptor isn't returning the next response because you've wrapped that part in then() without returning the outer promise.
Keep things simple with async / await everywhere.
Also, setting common headers defeats the point in using interceptors. You've already got a request interceptor, let it do its job
// wait for this to complete
await store.dispatch("setUser", { user, refresh: true })
// your token is now in the store and can be used by the request interceptor
// re-run the original request
return Vue.prototype.$axios.request(originalRequest)
Your store action also falls into the explicit promise construction antipattern and can be simplified
async setUser({ dispatch, commit }, { user, refresh }) {
if(user) {
try {
const token = await user.getIdToken(refresh);
commit('SET_SESSION_TOKEN', token);
try {
const { data } = await this._vm.$axios.get('/api/user/session');
commit('SET_SESSION_USER', data);
} catch (err) {
dispatch('logout');
dispatch('setSnackbar', {
color: "error",
timeout: 4000,
text: `Server unavailable: ${err.response?.data ?? err.message}`
})
}
} catch (err) {
dispatch('logout');
dispatch('setSnackbar', {
color: "error",
timeout: 4000,
text: `Unable to verify auth token. ${error}`
})
}
} else {
console.log('running logout');
commit('SET_SESSION_USER', null);
commit('SET_SESSION_TOKEN', null);
}
}

Flutter Firebase Stripe issue with json.decode

I try to implement Stripe on my Flutter Firebase project but I've got an issue.
Here's my code :
Future<void> makePayment () async {
final url = Uri.parse('***********');
final response = await http.get(url,
headers: {
'Content-Type': 'application/json'
});
paymentIntentData = json.decode(response.body);
// Here's the issue
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentData!['paymentIntent'],
applePay: true,
googlePay: true,
style: ThemeMode.dark,
merchantCountryCode: 'US',
merchantDisplayName: 'Test',
)
);
setState(() {
});
displayPaymentSheet();
}
Got this error :
FormatException (FormatException: Unexpected character (at character 1)
Error: could not handle the request
^
)
I've tried to search the reason it returns this error, but can not find. Please someone can explain me what I'm doing wrong ? I don't want bother you with too much code, if you need anything to help me understand where is my mistake, just let me know.
Hi you can check the Logs in Stripe Dashboard https://dashboard.stripe.com/logs and see if the Payment Intent is created successfully.

Firestore + Firebase Functions Working Slow or Returning too quick

A bit new to this so apologies if it's simple.
I have this firebase function below:
Some Context Code:
const functions = require("firebase-functions");
const cors = require("cors")({
origin: true,
});
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
Actual Function:
exports.testFunction = functions.https.onRequest((request, response) => {
cors(request, response, ()=>{
functions.logger.info(request.body, {structuredData: true});
const rbody = JSON.parse(request.body);
const egConst = rbody.messageDets;
egConst.sent = new Date().toUTCString();
const setM = db.collection("egColl")
.doc(rbody.egID)
.collection("messages")
.doc()
.set({
sent: egConst.sent,
user: egConst.user,
text: egConst.text,
})
.catch((error) => {
response.status(400).send({code: 400, body: "Error\n" + error});
});
setM.then(
response.status(200).send({code: 200, body: "Success"})
);
});
});
Now this kind of works as planned, but extremely slow.
Firstly, I get a 200 Response almost immediately, with speeds that I would expect, but the document is not created for another 40 seconds-1 minute.
So two parts...
Why do I get a 200 response before "setM" is done.
More importantly, why does it take so long to create the document? If I run a very similar version of this code on the client it is extremely quick, but I run into time sync issues.
Thank you in advanced for any help.
P.S. I'm planning to move it back to the client, but I would still like answers to further my understanding.
The problem is caused by these lines:
setM.then(
response.status(200).send({code: 200, body: "Success"})
);
Here, instead of passing a callback to the then() function, you are passing the result of the send() function. Because it's not a callback, you are sending back the 200 response code early. In addition, once a function has sent back a response, it is considered 'stale' and performance will drop significantly while it awaits to be shutdown by Cloud Functions. Any work after sending a response may not be completed. So make sure to do any work (like writing to databases, etc) before sending back the response. If you expect to do a long running task, write the task to a database (where it triggers another function) and send back a response to the client telling it that you've accepted the job and where to look for updates on its progress.
It should be:
setM.then(() => {
response.status(200).send({code: 200, body: "Success"})
});
or just:
setM.then(() =>
response.status(200).send({code: 200, body: "Success"})
);
However, in addition to this problem, you have chained these actions in the wrong order. By placing setM.then() where it is, you have effectively done this:
const setM = db.collection("egColl")
.doc(rbody.egID)
.collection("messages")
.doc()
.set({
sent: egConst.sent,
user: egConst.user,
text: egConst.text,
})
.catch((error) => {
response.status(400).send({code: 400, body: "Error\n" + error});
})
.then(() => {
response.status(200).send({code: 200, body: "Success"})
});
In the above code, if the set() call were to throw an error, the handler you passed to catch() would run as you expect and send back a 400 response, but in addition to that, it would also try to send the 200 response causing another error because you can't send back two different responses.
Next, in your current code you also parse the request's body manually, if you call this endpoint with a Content-Type of application/json (as you should be doing), the body will already be parsed for you.
This is roughly how I would write your function:
exports.testFunction = functions.https.onRequest((request, response) => {
cors(request, response, () => {
functions.logger.info(request.body, {structuredData: true});
const rbody = typeof request.body === "string" ? JSON.parse(request.body) : request.body;
const { messageDets, egID } = rbody;
const datetimeString = new Date().toUTCString();
const messageRef = db.collection("egColl")
.doc(egID)
.collection("messages")
.doc();
messageRef
.set({
sent: datetimeString,
user: messageDets.user,
text: messageDets.text,
})
.then(
() => {
functions.logger.info(`Created new message: egColl/${egID}/messages/${messageRef.id}`);
response.status(200).json({
code: 200,
message: 'Success',
resourceId: messageRef.id
})
},
(error) => {
functions.logger.error("Error whilst setting data", error);
response.status(400).send({
code: 400,
message: 'Error',
// don't leak stack traces!
// return Firebase error code instead of message if available
error: error.code || error.message
})
}
)
.catch((error) => {
// if everything is working as it should, this will never get called
// you could send this to it's own logger
functions.logger.error("Unexpected error while responding", error);
});
});
});
I would suggest refreshing the Firebase console but that most likely isn't the issue.
I would recommend restructuring your code like this and check if the issue still persists:
exports.testFunction = functions.https.onRequest((request, response) => {
cors(request, response, () => {
functions.logger.info(request.body, { structuredData: true });
const rbody = JSON.parse(request.body);
const egConst = rbody.messageDets;
egConst.sent = new Date().toUTCString();
return db.collection("egColl")
.doc(rbody.egID)
.collection("messages")
.doc()
.set({
sent: egConst.sent,
user: egConst.user,
text: egConst.text,
}).then(() => {
return response.status(200).send({ code: 200, body: "Success" })
})
.catch((error) => {
return response.status(400).send({ code: 400, body: "Error\n" + error });
});
});
});
or you would have to use async-await. The docRef.set() returns a promise so you would have to await it. But your existing code won't wait for it because you don't have await and hence will end up returning 200 even before the request is processed and terminate the cloud function.

google places api returns a string, how do I parse to JSON object?

In a small webshop that I am trying to setup, I need to update the opening hours in the background with firebase functions and google place details when a user creates a shoppingcart.
I can succesfully sent a GET request with POSTMAN to retrieve the opening hours of a shop using the following instructions:
https://developers.google.com/places/web-service/details
But I cannot access the response from the GET request as I usually do with JSON responses.
I tried also:response.result.opening_hours.json()
Can someone tell me what I am doing wrong?
export const mapGooglePlaces = functions.database
.ref('/shopping-carts/{shoppingCartId}/shippingManner')
.onWrite(event => {
const shippingManner = event.data.val();
const optionsAPI = {
method: 'GET',
uri: 'https://maps.googleapis.com/maps/api/place/details/json?placeid=ChIJN1t_tDeuEmsRUsoyG83frY4&key=YOUR_API_KEY',
};
return request(optionsAPI)
.then(response => {
const openingHours = response.result.opening_hours;
console.log(openingHours);
return;
})
.catch(function (err) {
console.log(err);
});
});
The response is not a JSON object. It is JSON formatted text and must be parsed to create an object. Modify the code as follows:
return request(optionsAPI)
.then(response => {
const responseObject = JSON.parse(response);
const openingHours = responseObject.result.opening_hours;
console.log(openingHours);
return;
})
.catch(function (err) {
console.log(err);
});
Also, before using the opening_hours or any other property of result, you should test responseObject.status === 'OK' to confirm that a place was found and at least one result was returned.

Resources