Reply sent email by Gmail API - ruby-on-rails

Currently I'm trying to create send and reply using gmail api, in documentation here
Refernces and In-Reply-To must be set in compliance with the RFC 2822 standard, the problem is when I try to get References and In-Reply-To from specified id like below:
`{
"id": "16183e0822247c79",
"threadId": "16183e0822247c79",
"labelIds": [
"SENT"
],
"snippet": "terkait",
"historyId": "1640387",
"internalDate": "1518335984000",
"payload": {
"partId": "",
"mimeType": "multipart/mixed",
"filename": "",
"headers": [
{
"name": "Received",
"value": "from 1059028371380 named unknown by gmailapi.google.com with HTTPREST; Sat, 10 Feb 2018 23:59:44 -0800"
},
{
"name": "Date",
"value": "Sat, 10 Feb 2018 23:59:44 -0800"
},
{
"name": "From",
"value": "jaisanas2#gmail.com"
},
{
"name": "To",
"value": "jaisanas3#gmail.com"
},
{
"name": "Message-Id",
"value": "\u003cCA+8aSZeXMOETdH8NYtd18UWk5eiQnvT0oEnEWy_1HL6mJPuKjw#mail.gmail.com\u003e"
},
{
"name": "Subject",
"value": "terkait"
},
{
"name": "Mime-Version",
"value": "1.0"
},
{
"name": "Content-Type",
"value": "multipart/mixed; boundary=\"--==_mimepart_5a7ff7f050e3_3263ffa0ceb1cc020ea\"; charset=UTF-8"
},
{
"name": "Content-Transfer-Encoding",
"value": "7bit"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=\"--==_mimepart_5a7ff7f05063_3263ffa0ceb1cc01916\"; charset=UTF-8"
},
{
"name": "Content-Transfer-Encoding",
"value": "7bit"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
},
{
"name": "Content-Transfer-Encoding",
"value": "7bit"
}
],
"body": {
"size": 14,
"data": "PHA-dGVya2FpdDwvcD4="
}
}
]
}
]
},
"sizeEstimate": 929
}`
When you see the result there are no headers In-Reply-To and Refernces, my questions is it is possible to reply email using API ?
Here is my code in ruby:
client = google_client user_id
token = Token.find_by_user_id(user_id)
access_token = token.access_token
gmail = Google::Apis::GmailV1::GmailService.new
gmail.authorization = client
message = Mail.new
message.date = Time.now
message.subject = "Re: #{subject}"
message.from = token.email
message.to = "#{to}"
# message.thread_id = "#{thread_id}"
message.message_id = "\u003cCA+8aSZeXMOETdH8NYtd18UWk5eiQnvT0oEnEWy_1HL6mJPuKjw#mail.gmail.com\u003e"
message.part content_type: 'multipart/alternative' do |part|
part.html_part = Mail::Part.new(body: "#{body}", content_type: 'text/html; charset=UTF-8')
end
msg = message.encoded
message_object = Google::Apis::GmailV1::Message.new(raw:message.to_s, thread_id: thread_id, content_type: 'message/rfc822')
gmail.send_user_message('me', message_object)
This code successfully send email in the same thread, but not reply email, here is what looks like inside my gmail sent emails:
As you can see, message with body lauv does not reply message terkait instead I just sent email lauv, my question is how to reply email?

Here you need to set In-Reply-To and References headers. If there is only one email in the thread use the message_id as In-Reply-To and References

Related

Paypal sandbox cancel webhook not working

The webhook for a subscription activation BILLING.SUBSCRIPTION.ACTIVATED in the Paypal's sandbox works very well.
Nevertheless, when I'm trying to cancel the subscription from a customer sandbox account, no webhook post is received.
It should be BILLING.SUBSCRIPTION.CANCELLED.
In fact, the status is "pending" from the sandbox admin account:
Does it wait until the subscription's end date in order to send the cancellation?
Or does it wait a few hours for safety reasons?
Here are the details from the paypal sandbox web site:
{
"id": "WH-3BV96304FY002321K-38Y64104YL460680C",
"create_time": "2022-07-04T17:51:34.874Z",
"resource_type": "subscription",
"event_type": "BILLING.SUBSCRIPTION.CANCELLED",
"summary": "Subscription cancelled",
"resource": {
"quantity": "1",
"subscriber": {
"email_address": "valid_adress#yahoo.fr",
"payer_id": "X3FWGFE2SAXUL",
"name": {
"given_name": "Anyname",
"surname": "Walker"
},
"shipping_address": {
"address": {
"address_line_1": "Great st 25",
"admin_area_2": "New York",
"admin_area_1": "NY",
"postal_code": "10002",
"country_code": "US"
}
}
},
"create_time": "2022-07-04T17:49:00Z",
"plan_overridden": false,
"shipping_amount": {
"currency_code": "USD",
"value": "0.0"
},
"start_time": "2022-07-04T17:46:51Z",
"update_time": "2022-07-04T17:51:25Z",
"billing_info": {
"outstanding_balance": {
"currency_code": "USD",
"value": "0.0"
},
"cycle_executions": [
{
"tenure_type": "REGULAR",
"sequence": 1,
"cycles_completed": 1,
"cycles_remaining": 0,
"current_pricing_scheme_version": 1,
"total_cycles": 0
}
],
"last_payment": {
"amount": {
"currency_code": "USD",
"value": "3.99"
},
"time": "2022-07-04T17:49:01Z"
},
"failed_payments_count": 0
},
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-6BR7NSX98KLF",
"rel": "self",
"method": "GET",
"encType": "application/json"
}
],
"id": "I-6BR7NSX98KLF",
"plan_id": "P-5XR3459593FMK2CYGA",
"status": "CANCELLED",
"status_update_time": "2022-07-04T17:51:25Z"
},
"status": "PENDING",
"transmissions": [
{
"webhook_url": "https://www.myapp.com/webhook",
"http_status": 440,
"reason_phrase": "HTTP/1.1 200 Connection established",
"response_headers": {
"Server": "gunicorn",
"Connection": "keep-alive",
"Content-Length": "5",
"Date": "Mon, 04 Jul 2022 18:15:52 GMT",
"Content-Type": "text/html; charset=utf-8",
"Via": "1.1 google"
},
"transmission_id": "02cba350-fbc2-9abc-d537a3be4540",
"status": "PENDING",
"timestamp": "2022-07-04T17:52:03Z"
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-3BV96304FY002321K-38Y64104YL460680C",
"rel": "self",
"method": "GET",
"encType": "application/json"
},
{
"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-3BV96304FY002321K-38Y64104YL460680C/resend",
"rel": "resend",
"method": "POST",
"encType": "application/json"
}
],
"event_version": "1.0",
"resource_version": "2.0"
}
Pending means pending delivery to the subscribed webhook URL. The event ocurred, and delivery to the URL was attempted, but that URL did not respond with an HTTP 200 success message for the webhook to be marked as successfully delivered. In fact, according to the details logged in your question:
"webhook_url": "https://www.myapp.com/webhook",
"http_status": 440,
That URL instead responded with an HTTP 440 status
Assuming the URL is correct and that you changed it to "myapp" for this question, the issue is that you need to debug the endpoint/code at that URL so that it responds with a 200 success status when webhook deliveries are posted to it. Then their status will stop being Pending.

Bad Request "ErrorExecuteSearchStaleData" in Microsoft Graph API - Search for Mails

We are using Microsoft Graph Search API to search through our O365 emails. Since the search only allows 25 results per request for mails. (see https://learn.microsoft.com/en-us/graph/api/resources/search-api-overview?view=graph-rest-beta#page-search-results)
We figured to work around this by batching our search request like this:
POST https://graph.microsoft.com/beta/$batch
{
"requests": [
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-0",
"body":
{
"requests": [
{
"entityTypes": ["message"],
"query":
{
"query_string":
{
"query": "some search text"
}
},
"from": 0,
"size": 25
}
]
},
"headers":
{
"Content-Type": "application/json"
}
},
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-1",
"body":
{
"requests": [
{
"entityTypes": ["message"],
"query":
{
"query_string":
{
"query": "some search text"
}
},
"from": 25,
"size": 25
}
]
},
"headers":
{
"Content-Type": "application/json"
}
},
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-2",
"body":
{
"requests": [
{
"entityTypes": ["message"],
"query":
{
"query_string":
{
"query": "some search text"
}
},
"from": 50,
"size": 25
}
]
},
"headers":
{
"Content-Type": "application/json"
}
},
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-3",
"body":
{
"requests": [
{
"entityTypes": ["message"],
"query":
{
"query_string":
{
"query": "some search text"
}
},
"from": 75,
"size": 25
}
]
},
"headers":
{
"Content-Type": "application/json"
}
}
]
}
This works usually well, but in some cases the API returns something like this:
{
"id": "MyMails-2",
"status": 400,
"headers":
{
"Link": "<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,RemoveDeprecatedUnderscoreProperty&from=2021-12-01&to=2022-01-01>;rel=\"deprecation\";type=\"text/html\",<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,RemoveDeprecatedUnderscoreProperty&from=2021-12-01&to=2022-01-01>;rel=\"deprecation\";type=\"text/html\",<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,RemoveDeprecatedUnderscoreProperty&from=2021-12-01&to=2022-01-01>;rel=\"deprecation\";type=\"text/html\"",
"Deprecation": "Tue, 14 Dec 2021 23:59:59 GMT",
"Sunset": "Sat, 31 Dec 2022 23:59:59 GMT",
"Cache-Control": "no-cache",
"Content-Type": "application/json"
},
"body":
{
"error":
{
"code": "BadRequest",
"message": "\r\n An exception occurred\r\n Lss call failed with status code 400. \"Exchange service returned error ErrorExecuteSearchStaleData: Please reissue the query with rowOffset = 0. The specified rowoffset is '50', but the results are stale.\".",
"innerError":
{
"date": "2022-01-25T09:58:53",
"request-id": "75def95f-a857-427d-a8b4-ee2792329e87",
"client-request-id": "75def95f-a857-427d-a8b4-ee2792329e87"
}
}
}
}
I noticed the deprecation note in the header, however I can't find anything about it. Neither about the actual exception.
What am I missing?
[workaround]
Thanks for the hint #user2250152.
This heavily reduced the issue by splitting the request into two requests, while the second is able to request more than 25 mails at a time:
POST https://graph.microsoft.com/beta/$batch
{
"requests": [
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-0",
"body": {
"requests": [
{
"entityTypes": [
"message"
],
"query": {
"query_string": {
"query": "some search text"
}
},
"from": 0,
"size": 25
}
]
},
"headers": {
"Content-Type": "application/json"
}
},
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-1",
"body": {
"requests": [
{
"entityTypes": [
"message"
],
"query": {
"query_string": {
"query": "some search text"
}
},
"from": 25,
"size": 200
}
]
},
"headers": {
"Content-Type": "application/json"
}
}
]
}
The error with stale results can happen time to time.
You can decrease the number of batch requests to reduce a chance that the error with stale results will occur.
For the first page "from": 0 the max size is 25. But for the next page "from": 25 you can increase the page size to 200.
I've tested the search query with "from": 25 and "size": 200 and it returns 200 results.
Resources:
Page search results

Create message in Exchange causes "UnableToDeserializePostBody"

I have some code that was working just fine a few months ago, but something in the Graph API has changed and this no longer works. I am trying to create a message in an existing folder, by doing a POST to this url:
https://graph.microsoft.com/v1.0/users/jjones#transend.onmicrosoft.com/mailFolders/AAMkADNjAAA=/messages
(folder id shortened)
The call fails with http error 400, and the returned error is "UnableToDeserializePostBody". The input json is shown below. By experimentation I was able to trace the problem specifically to "singleValueExtendedProperties". I normally put several properties there, but for this test I removed all but the one you see. I have tried other properties as well, they all fail. This seems like some stupid formatting error but I can't see it. Any help appreciated.
{
"subject": "Test again",
"Sender": {
"emailAddress": {
"name": "John Doe",
"address": "missing#domain.com"
}
},
"body": {
"contentType": "TEXT",
"content": "This is a text message."
},
"toRecipients": [
{
"emailAddress": {
"name": "Jane Smith",
"address": "missing#domain.com"
}
}
],
"ccRecipients": [
{
"emailAddress": {
"name": "Bob Jones",
"address": "missing#domain.com"
}
}
],
"singleValueExtendedProperties": [
{
"propertyId": "SystemTime 0x0039",
"value": "1998-07-29T21:30:00.0000+00:00"
}
],
"importance": "normal"
}
The main problem here is you are specifying the property('propertyid') in singleValueExtendedProperties object is not valid. There are only 2 properties in singleValueExtendedProperties. One is id and the other is value.
Replace 'propertyId' with id.
I have tested it in POSTMAN with your payload changing the propertyId to id and it worked.
Request Body:-
{
"subject": "Test again",
"Sender": {
"emailAddress": {
"name": "John Doe",
"address": "missing#domain.com"
}
},
"body": {
"contentType": "TEXT",
"content": "This is a text message."
},
"toRecipients": [
{
"emailAddress": {
"name": "Jane Smith",
"address": "missing#domain.com"
}
}
],
"ccRecipients": [
{
"emailAddress": {
"name": "Bob Jones",
"address": "missing#domain.com"
}
}
],
"singleValueExtendedProperties": [
{
"id": "SystemTime 0x0039",
"value": "1998-07-29T21:30:00.0000+00:00"
}
],
"importance": "normal"
}

How do I get browsermob-proxy to record ALL response bodies

I'm having trouble with browsermob-proxy and its har exporting feature. Some response bodies are not logged (the entire "text" field is missing)
My setup (using browsermob-proxy 2.1.4)
curl -X POST http://localhost:8080/proxy?port=9091
curl -X PUT "http://localhost:8080/proxy/9091/har?captureHeaders=true&captureCookies=true&captureContent=true"
Some responses are fine:
"response": {
"status": 201,
"statusText": "Created",
"httpVersion": "HTTP/1.1",
"cookies": [],
"headers": [{
"name": "Cache-Control",
"value": "max-age=0, no-cache, no-store"
}, {
"name": "Content-Type",
"value": "application/json"
}, {
"name": "Date",
"value": "Thu, 15 Feb 2018 13:07:39 GMT"
}, {
"name": "Location",
"value": ...
}, {
"name": "Pragma",
"value": "no-cache"
}, {
"name": "Render-Time",
"value": "8"
}, {
"name": "Server",
"value": "openresty"
}, {
"name": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
}, {
"name": "transfer-encoding",
"value": "chunked"
}, {
"name": "Connection",
"value": "keep-alive"
}],
"content": {
"size": 8607,
"mimeType": "application/json",
"text": "{ <actual json body> }",
"comment": ""
}, ...
But some are not (maybe it is the special content/mime type? or maybe it is the gzip content encoding?)
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "HTTP/1.1",
"cookies": [],
"headers": [{
"name": "Cache-Control",
"value": "max-age=0, no-cache, no-store"
}, {
"name": "Content-Encoding",
"value": "gzip"
}, {
"name": "Content-Type",
"value": "application/some.custom.type-v1+json"
}, {
"name": "Date",
"value": "Thu, 15 Feb 2018 13:07:39 GMT"
}, {
"name": "Pragma",
"value": "no-cache"
}, {
"name": "Render-Time",
"value": "92"
}, {
"name": "Server",
"value": "openresty"
}, {
"name": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
}, {
"name": "Vary",
"value": "Accept-Encoding"
}, {
"name": "Content-Length",
"value": "1978"
}, {
"name": "Connection",
"value": "keep-alive"
}],
"content": {
"size": 7429,
"mimeType": "application/some.custom.type-v1+json",
"comment": ""
< there's nothing else here!! >
},
"redirectURL": "",
"headersSize": 444,
"bodySize": 1978,
"comment": ""
}, ...
Oh yea, both requests are over https, using MITM.
I think I found the answer to my own question
In BrowserMobHttpClient.java I found the following:
private boolean hasTextualContent(String contentType) {
return contentType != null && contentType.startsWith("text/") ||
contentType.startsWith("application/x-javascript") ||
contentType.startsWith("application/javascript") ||
contentType.startsWith("application/json") ||
contentType.startsWith("application/xml") ||
contentType.startsWith("application/xhtml+xml");
}
Looks like I'll have go make a custom build of browsermob-proxy to make it work.

Swagger - Set the "response" based on "consumes"

Is it possible to specify the "response" based on the "consumes" option on Swagger?
My API can return "json" or "text" and I'd like that the "example value", when the response's status is 200 changes if the user selects from Response Content Type the option application/json or text/plain.
This is a piece of my swagger.json file:
{
"swagger": "2.0",
"produces": [
"application/json",
"text/plain"
],
"consumes" : [
"application\/json"
],
"paths": {
"/writer/1/": {
"get": {
"summary": "Get all writers",
"description": "Writer description.",
"tags": [
"writer"
],
"responses": {
"200": {
"description" : "successful operation",
"schema" : {
"$ref" : "#\/definitions\/writerResponse"
}
},
}
}
}
"definitions": {
"writerResponse": {
"properties": {
"meta": {
"type" :"object",
"schema" : {
"$ref" : "#\/definitions\/metaDefinition"
}
},
"data": {
"type" :"array",
"items": {
"$ref" : "#\/definitions\/writer"
}
}
}
},
"writer": {
"properties": {
"id": {
"type": "integer",
"description": "writer id.",
"example": "1477"
},
"short": {
"type": "string",
"description": "short description.",
"example": "short example"
},
"modified": {
"type": "string",
"description": "modified description.",
"example": "2016-05-21 22:58:36"
}
}
},
}
This is an example of the JSON output when user selects application/json:
{
"meta": {
"total": "1234",
"last_page": "967",
"per_page": "4000",
"current_page": "1",
"next_page_url": "http://localhost/api/<ws>/1?page=2",
"prev_page_url": "http://localhost/api/<ws>/1?page=1",
"from": "1",
"to": "4000"
},
"data": [
{
"id": "1",
"short": "TEST1",
"modified": "2011-03-07 14:17:23"
},
{
"id": "5",
"short": "TEST2",
"modified": "2015-05-26 12:39:45"
}
]
}
And this is the output when the user selects text/plain:
id|short|modified
1|TEST1|2011-03-07 14:17:23
5|TEST2|2015-05-26 12:39:45

Resources