I am trying to upload an image to twitter from another website and am using Oauth. I am able to login my twitter account however, with unable to post an image to my account. I know the image initially needs to be a Base64 encoded image but the code I have now give me the following error "Object {readyState: 4, responseText: "{"request":"/1.1/media/upload.json","error":"media type unrecognized."}", responseJSON: Object, status: 400, statusText: "Bad Request"}"
Here is my code
`OAuth.popup('twitter')
.done((result) => {
result.post('https://upload.twitter.com/1.1/media/upload.json', {
data: {
media_data: 'data:image/gif;base64,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',
},
})
.done((response) => {
console.log(response);
})
.fail((err) => {
console.log(err);
});
})
.fail((err) => {
console.log(err);
});
`
Any ideas on what I am doing wrong?
The media_data value should have the data:image/gif;base64, prefix removed. To account for multiple file types, you should split at the comma, resulting in an array of length 2, and use the 2 item in the array for the call to OAuth.io/Twitter.
Related
I'm implementing the Forgot password feature using truevault API. Now, I've been testing the requests following the flow with Postman, and it works, but, when I started coding using axios, it keeps throwing issues about authentication. I've tried several combinations (logical ones, not just random craziness).
Also, worth mentioning that I was able to list my truevault users from UI (not only postman), and tried to mimic the same principle to the post request, but it didn't work
Here is the postman request that worked for me:
for the url request, method is: POST
url: https://api.truevault.com/v1/password_reset_flows
For the Authorization tab, I filled the "username" field with the truevault user API Key, and left the "password" field empty
And the "Body" tab, I filled it with a Json text, and for radio button options, I selected raw, and picked json as the format. (these are the only tabs being used)
The json body is as follow
{
"name":"XXXXX password reset",
"sg_template_id":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXcf42",
"sg_api_key":"XX.XXXXXXXXXXXXXXXXXXXXXX.XXXXXX_XXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXZftJo",
"user_email_value_spec":{
"system_field":"username"
},
"from_email_value_spec":{
"literal_value":"do-not-reply#XXXXXX.com"
},
"substitutions":{
"{{FIRST_NAME}}":{
"user_attribute":"first_name"
}
}
}
And the result was successful,
Now, when I tried with axios, I kept getting the auth error. Code is as follows:
createPasswordResetFlow()
{
axios.defaults.headers.common["Authorization"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX27"; //tv user API KEY
axios.defaults.headers.post["Content-Type"] = "application/json";
var request = axios.post("https://api.truevault.com/v1/password_reset_flows",
{
auth:
{
username: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX27',
password: ""
},
data:
{
"name": "XXXXX password reset",
"sg_template_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXcf42",
"sg_api_key": "XX.XXXXXXXXXXXXXXXXXXXXXX.XXXXXX_XXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXZftJo",
"user_email_value_spec":
{
"system_field": "username"
},
"from_email_value_spec":
{
"literal_value": "do-not-reply#XXXXXX.com"
},
"substitutions":
{
"{{FIRST_NAME}}":
{
"user_attribute": "first_name"
}
}
}
})
.then((res) =>
{
console.log(res);
return res.data.users;
})
.catch(error =>
{
console.log('error', error);
return error;
});
}
As mentioned also earlier, I've been researching and trying, but to no avail, if someone could help me please.
There are two issues with the JS code you shared which are causing the problem:
The line where you set the default Auth header looks like this: axios.defaults.headers.common["Authorization"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX27"; //tv user API KEY. Note that the Authorization header is being set to the API key, not an HTTP Basic Auth header value. If you want to set the default auth header this way, you need to set it to base64(API_KEY:) rather than just API_KEY.
According to the axios docs the post method has the signature .post(url, data, config). As a result, your code is POSTing a JSON object that looks like {auth: ..., data: ...}.
Try removing the line where you set the authorization header, and changing the post call to look something like this:
axios.post("https://api.truevault.com/v1/password_reset_flows",
{
"name": "XXXXX password reset",
"sg_template_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXcf42",
"sg_api_key": "XX.XXXXXXXXXXXXXXXXXXXXXX.XXXXXX_XXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXZftJo",
"user_email_value_spec":
{
"system_field": "username"
},
"from_email_value_spec":
{
"literal_value": "do-not-reply#XXXXXX.com"
},
"substitutions":
{
"{{FIRST_NAME}}":
{
"user_attribute": "first_name"
}
}
}
}, {
username: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX27',
password: ""
})
I've been playing around with stripe and would like to learn how to get ephemeral keys in the following way:
Back-end:
//Stripe API requirement for payment
exports.stripeEphemeralKey = functions.https.onCall((data, context) => {
const uid = context.auth.uid;
//Get values
admin.database().ref().child("users").child(uid)
.on("value", (snapshot) =>{
//Get user data
let user = snapshot.val()
//Log data
console.log("Create ephemeral key for:")
console.log(user)
//Create ephemeral key
stripe.ephemeralKeys.create(
{customer: user.customerid },
{stripe_version: '2018-11-08'}
)
.then((key) => {
console.log(key)
console.log("Succesful path. Ephemeral created.")
return "Testing"
})
.catch((err) => {
console.log("Unsuccesful path. Ephemeral not created.")
console.log(err)
return {
valid: false,
data: "Error creating Stripe key"
}
})
})
})
Client-side:
functions.httpsCallable("stripeEphemeralKey").call(["text": "Testing"]) { (result, error) in
print(result?.data)
}
I have tested this code by replacing the body of the stripeEphemeralKey with a simple "Testing" string and that returns just fine. But with the code above I just get Optional() back.
For testing I added lots of console logs. Firebase logs show the execution path gets to the "Succesful path. Ephemeral created." log, and furthermore I can actually see the ephemeral key I get back from stripe.
So, what is the proper correct way to get the ephemeral key in Swift for iOS using the onCall Firebase function?
The backend does what it should, but I can't seem to get the answer back.
Thank you.
The backend does not actually do what it should do. You're doing at least two things wrong here.
First, your callable function needs to return a promise that resolves with the value that you want to send to the client. Right now, your function callback isn't returning anything at all, which means the client won't receive anything. You have return values inside promise handlers, but you need a top-level return statement.
Second, you're using on() to read data from Realtime Database, which attaches a listener that persists until it's removed. This is almost certainly never what you want to do in a Cloud Function. Instead, use once() to get a single snapshot of the data you want to read, and act on that.
For my own reference, and those who might find this helpful:
//Stripe API requirement for payment
exports.stripeEphemeralKey = functions.https.onCall((data, context) => {
const uid = context.auth.uid;
return admin.database().ref().child("users").child(uid)
.once("value", (snapshot) =>{
console.log(snapshot.val() )
})
.then( (snap) => {
const customer = snap.val()
//Log data
console.log("Create ephemeral key for:")
console.log(customer)
//Create ephemeral key
return stripe.ephemeralKeys.create(
{customer: customer.customerid },
{stripe_version: '2018-11-08'}
)
.then((key) => {
console.log(key)
console.log("Succesful path. Ephemeral created.")
return {
valid: true,
data: key
}
})
.catch((err) => {
console.log("Unsuccesful path. Ephemeral not created.")
console.log(err)
return {
valid: false,
data: "Error creating Stripe key"
}
})
})
.catch( err => {
console.log("Unsuccesful path. Ephemeral not created.")
console.log(err)
return {
valid: false,
data: "Error gettting customerid"
}
})
})
The key seems to be to chain the initial database request with .then(), and do our our work chaining the returns uninterrupted as we use functions that return promises. In particular, placing my work-code inside the callback on the original admin.database().ref().once() function did not work for me.
I am new to this kind of programming, so someone who knows about this might the why better.
I am building an vueJs application with a service worker. I decided to use Workbox with an InjestManifest method to had my own routes.
on fetch when online :
1- answer with the network
2- wrtting body to IDB (through localforage)
3- send back the response
here everything is working perfectly, the sw intercepts the fetch and come back with an appropirate response, IDB contains rigth details.
response sent back to fecth when online:
Response {type: "cors", url: "http://localhost:3005/api/events", redirected: false, status: 200, ok: true, …}
the issue is when I go offline.
my intention id to connect to Locaforage and retrieve the content and build a response.
The issue is that this response is not considered as appropriate by Fetch who then reject it. Console.log confirms that the .catch in sw is working but it looks like the response it sends is rejected.
here is the console.log of the response I am sending back to fetch when offline;
Response {type: "default", url: "", redirected: false, status: 200, ok: true, …}
I do not know if fetch is not happy becasue the url of the repsonse is not the same as on the request but workbox is supposed to allow responding with other resposnes than the ones coming from cache or fetch.
here is the code
importScripts('localforage.min.js')
localforage.config({
name: 'Asso-corse'
})
workbox.skipWaiting()
workbox.clientsClaim()
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
cacheName: 'googleapis',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 30
})
]
})
)
workbox.routing.registerRoute( new RegExp('http://localhost:3005/api/'), function (event) {
fetch(event.url)
.then((response) => {
var cloneRes = response.clone()
console.log(cloneRes)
cloneRes.json()
.then((body) => {
localforage.setItem(event.url.pathname, body)
})
return response
})
.catch(function (error) {
console.warn(`Constructing a fallback response, due to an error while fetching the real response:, ${error}`)
localforage.getItem(event.url.pathname)
.then((res) => {
let payload = new Response(JSON.stringify(res), { "status" : 200 ,
"statusText" : "MyCustomResponse!" })
console.log(payload)
return payload
})
})
})
workbox.precaching.precacheAndRoute(self.__precacheManifest || [])
I am really stuck there as all documentation on workbox relates to leveraging cache. I am leveraging localforage as it supports promises which is what is required to make offline capability working.
Thanks
Your catch() handler needs to return either a Response object, or a promise for a Response object.
Adjusting the formatting of your sample code a bit, you're currently doing:
.catch(function (error) {
console.warn(`Constructing a fallback response, due to an error while fetching the real response:, ${error}`)
localforage.getItem(event.url.pathname).then((res) => {
let payload = new Response(JSON.stringify(res), { "status" : 200 , "statusText" : "MyCustomResponse!" })
console.log(payload)
return payload
})
})
Based on that formatting, I think it's clearer that you're not returning either a Response or a promise for a Response from within your catch() handler—you're not returning anything at all.
Adding in a return before your localforage.getItem(...) statement should take care of that:
.catch(function (error) {
console.warn(`Constructing a fallback response, due to an error while fetching the real response:, ${error}`)
return localforage.getItem(event.url.pathname).then((res) => {
let payload = new Response(JSON.stringify(res), { "status" : 200 , "statusText" : "MyCustomResponse!" })
console.log(payload)
return payload
})
})
But, as mentioned in the comments to your original question, I don't think that using IndexedDB to store this type of URL-addressable data is necessary. You can just rely on the Cache Storage API, which Workbox will happily use by default, when storing and retrieving JSON data obtained from an HTTP API.
I am using react-native-firebase to work with our Firebase account for authentication, firestore and storage. Attempting to upload a photo to Storage is failing with an unknown error. Here is the code attempted:
_pickImage = async () => {
await this.getCameraRollPermission()
let result = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [4, 3],
});
console.log(result);
if (!result.cancelled) {
// this.setState({ photoURL: result.uri });
this._handlePhotoChoice(result)
}
};
_handlePhotoChoice = async pickerResult => {
let userId = this.state.userId
firebase
.storage()
.ref('photos/profile_' + userId + '.jpg')
.putFile(pickerResult.uri)
.then(uploadedFile => {
console.log("Firebase profile photo uploaded successfully")
})
.catch(error => {
console.log("Firebase profile upload failed: " + error)
})
}
Testing in iOS Simulator and using the debugger to detect the errors I'm just getting back this error:
"Error: An unknown error has occurred.
at createErrorFromErrorData (blob:http://localhost:19001/e9d43477-4e42-4f7a-b494-16485def4c28:2371:17)
at blob:http://localhost:19001/e9d43477-4e42-4f7a-b494-16485def4c28:2323:27
at MessageQueue.__invokeCallback (blob:http://localhost:19001/e9d43477-4e42-4f7a-b494-16485def4c28:2765:18)
at blob:http://localhost:19001/e9d43477-4e42-4f7a-b494-16485def4c28:2510:18
at MessageQueue.__guardSafe (blob:http://localhost:19001/e9d43477-4e42-4f7a-b494-16485def4c28:2678:11)
at MessageQueue.invokeCallbackAndReturnFlushedQueue (blob:http://localhost:19001/e9d43477-4e42-4f7a-b494-16485def4c28:2509:14)
at http://localhost:19001/debugger-ui/debuggerWorker.js:70:58"
UPDATE:
A file is uploaded to the storage bucket, but the file is not the JPEG photo, but instead is JSON content about the file:
{"contentType":"image\/jpeg","name":"photos\/profile_XPIO2lHjlYbdLPchACZHBsmY9Jr1.jpg"}
So somehow a JSON file is ending up in the bucket instead of the actual photo and then the error is thrown.
It looks like this issue is tracked a couple times, but not resolved:
https://github.com/invertase/react-native-firebase/issues/1177
https://github.com/invertase/react-native-firebase/issues/302
Finally found my issue. The URI of the image from the ImagePicker had a '%' character in it from the local app cache. This percent was being URI encoded to '%25' which resulted in the file not being found by the putFile code. Adding a decodeURI call around the uri fixed the issue.
let fileUri = decodeURI(pickerResult.uri)
In case you are using react-native-document-picker, check out this:
https://github.com/rnmods/react-native-document-picker/issues/235
I need to send a Twitter DM with quick reply.
I use Tweetinvi, that at the moment does not support quick replies, therefore I tried to alter the query in Tweetinvi code at the lovest possible level: just before it is sent to Twitter.
If I send this (basic message)
https://api.twitter.com/1.1/direct_messages/new.json?text=hello&user_id=999999999
It works
When I send is this
https://api.twitter.com/1.1/direct_messages/new.json?text=MessageToUserId&user_id=999999999&quick_reply&type=options&options=[label=RedBird&description=Adescriptionabouttheredbird.&metadata=external_id_1]
I get status 401 Web request failed.
To build my request I tried to simplify this example:
https://developer.twitter.com/en/docs/direct-messages/quick-replies/api-reference/options
But I am missing something. I suppose it is a trivial mistake in the query. I tried several variations, but I cannot get a better result. Of course in my code I use a real userId, that here I masked with 9.
Can you suggest me a working correction to my query? (maybe with examples using multiple labels)
Update.
I tried to use TwitterAccessor (without hacks in Tweetinvi code) and improved the json
Here is my updated code
Auth.SetUserCredentials(consumerKey, consumerSecret, userAccessToken, userAccessSecret);
var authenticatedUser = User.GetAuthenticatedUser();
var qString = JsonConvert.DeserializeObject("{ 'event': { 'type': 'message_create', 'message_create': { 'target': { 'recipient_id': '123456789' }, 'sender_id': '987654321', 'message_data': { 'text': 'option?', 'quick_reply': { 'type': 'options', 'options': [ { 'label': 'option 1', 'metadata': 'val1', 'description': 'option 2' }, { 'label': 'val2', 'metadata': 'option 3', 'description': 'val3' } ] } } } } }");
var strEncoded = WebUtility.HtmlEncode(qString.ToString());
var url = "https://api.twitter.com/1.1/direct_messages/events/new.json";
var result = TwitterAccessor.TryExecutePOSTQuery($"{url}?{strEncoded}");
Now I have error 401, with this description:
"Unauthorized - Authentication credentials were missing or incorrect."
Which is much better. Probably I just need to add credential headers using TwitterAccessor, but I need help about this.
I suppose that adding something TwitterAccessor. Method to create credentials headers before the post should do the job.