Is this a proper implementation of PUT idempotency and what should the response be? - asp.net-mvc

The way I have understood idempotency thus far is basically: If I send 10 identical PUTs to a server the resulting additional resources created will be identical to if I had sent a single PUT statement.
What I take this to mean is that the following implementation would adhere to this:
[AcceptVerbs(HttpVerbs.Put)]
ContentResult User(){
//parse XML that was sent to get User info
//User has an e-mail address which is unique to the system
//create a new user in the system only if one for this e-mail address does not exist
return Content(something, "text/xml");
}
There now if I sent 10 PUTs with XML for User data and they all contain the same e-mail address, only one user will be created.
However, what if they send 10 requests (for whatever reason) and they are all different, but the e-mail is the same. If the first request doesn't make it through then the data of the 2nd request will be used to create the user, and the following 8 requests will be ignored. Is there a flaw here? Or should I literally only ignore requests that are explicitly identical in every way and instead send back an error saying the user already exists if they use the same e-mail address?
Also, what kind of response should be sent from a such PUT statement? Info about the user? Maybe an ID to manipulate them with other API calls? Or perhaps it should just say "success" or "fail: [error details]"?

Your question doesn't reveal the URL where the PUT request is sent to. This is actually very important as it is not the email address within the XML data that dictates whether a new resource is created or an old one updated but the URL that you are sending the request to.
So, if you send PUT to /users/jonh.doe#foo.com/ it either creates the user john.doe#foo.com or updates it if it was already in the system.
Similaraly, if you send PUT to /users/123/ (using id instead of email) it will create or update user 123. However, in this case if the email has to be unique and somebody sends PUT /users/456/ and within that XML is the same email as what the user 123 already has, you have to respond with 409 Conflict.

If the user already exists with the same email address, then the 2nd and subsequent PUT operations should update the data for that resource. The success or failure should be communicated in the status code. If the update succeeds, respond with "200 OK", or "204 No Content"; you can return some information, but don't expect caches to store it as if it were the new representation you would obtain from a GET. If you do not intend for that resource to ever accept a PUT operation other than the first one, then respond instead with "405 Method Not Allowed", with an explanation in the response body. Use "409 Conflict" (again, with an explanation in the response body) if the submitted representation might replace the resource, but can't because it's particular fields cannot be reconciled with the existing state.

Related

How to remove Ephemeral Messages

I'm trying to figure out the mechanism to post an ephemeral message to a user and then remove it and replace it with a message visible to all. Similar behavior to giphy in which the Slash Command shows an interactive ephemeral message and creates a channel message once the user decides which gif to send. I'm also curious about updating the ephemeral message. I assume this can be done by the response_url if we use an interactive ephemeral message.
I initially figured I'd just create a ephemeral message using chat.postEphemeral and then call chat.delete on it, but it seems chat.delete and chat.update can't be called on a message created using chat.postEphemeral.
The Slack message guidelines seems to suggest that a multi-step interactive flow should always be handled in an ephemeral way so that other channel user don't see all intermediate messages before the result but I'm having bad luck figuring out how to get rid of the ephemeral when done. Probably just being bad at reading but any help appreciated.
Edit with more details:
The documentation around using response_url and postEphemeral states
As you replace messages using chat.update or the replace_original
option, you cannot change a message's type from ephemeral to
in_channel. Once a message has been issued, it will retain its
visibility quality for life.
The message guidelines suggest:
If a user has initiated an action that has multiple steps, those steps
should be shown as ephemeral messages visible only to that user until
the entire action is complete to avoid cluttering the channel for
everyone.
Presumably, I should be able to create an interaction in which I first send an in_channel interactive message.
When a user initiates an action, I should be able to send them a series of ephemeral messages using the response_url and passing response_type: 'ephemeral' and replace_original: false?
A new ephemeral interactive message created this way will have its own response_url for making edits, right?
Once I am done with the interactive flow via ephemeral messages, I can modify the original interactive message using its original response_url?
Lastly, how do I get rid of the last ephemeral edit? Or do I just change it to something like "Workflow completed" and hope for the best? I'm asking because Slash commands obviously seem to have a way to essentially replace the ephemeral message for an in_channel message and I'm trying to figure this kind of workflow out.
I searched high and low on how to do this and finally came across the answer.
Your ephemeral message must trigger an action, i.e. button click.
Your response to the action must use the following body
{
'response_type': 'ephemeral',
'text': '',
'replace_original': true,
'delete_original': true
}
'delete_original': true is the key here, which as far as I can tell is not mentioned in any of the API guides, however it is present in the API field guide under Top-level message fields
If you wish to change the response_type of your message instead of deleting it, you must do so by first deleting the ephemeral message and then posting the same message with 'response_type': 'in_channel'.
In my use case I wanted to take an ephemeral message and repost it with the exact same message body as an in-channel message. I have not found a way to retrieve the content of your ephemeral message, so the best method I've found is to pass whatever necessary data spawned your ephemeral message in the button's value so that your action handler can read this data and dynamically recreate the message body.
In my case, this was the user input being used to perform a query. On the off chance that data in the database changes between the time the original ephemeral message is posted and the in-channel version is posted they will be different. You may be able to send a JSON string directly through this value field and avoid making additional database calls and running the risk of messages changing when posted to the channel. The character limit of value is 2000 so JSON passing is extremely limited.
Assuming you use the same code to generate this body when initially creating the ephemeral message and also when recreating it in-channel, you should receive the same body and essentially are able to change an ephemeral message to in-channel message.
Some ephemeral messages can be "soft" deleted/replaced but only when posted as part of a message with interactive features like buttons or menus. When a button is clicked or a menu selection made, you have a chance to instruct Slack to either "delete" the original message, or replace it with a new one. These docs detail using responses and response_url to accomplish that.
A message created with chat.postEphemeral that itself has no interactive features can never be explicitly deleted. Once it's delivered, it's like a ghost and will disappear following a restart or refresh.
Answering your bulleted questions in order:
Correct, you essentially start a new chain of interactivity with net new ephemeral message you post to that user
Each interactive message interaction will have its own response URL. The new ephemeral message won't have a response_url you can use until the end user presses a button, selects a menu item, etc.
response_url will eventually expire ("using the response_url, your app can continue interacting with users up to 5 times within 30 minutes of the action invocation.") If the original message is non-ephemeral, using chat.update is a better strategy for longer timelines. With ephemeral messages, it's more of a "do your best" strategy. They'll eventually get cleaned up for the user after a refresh.
I think you have a good handle on what's best. Personally, I think it's easier to kick off a new "in_channel" message by using chat.postMessage instead of as a chain effect directly from a slash command or interaction.
The Kotlin/Java version for this solution using the Bolt API as shown below
import com.slack.api.bolt.handler.builtin.BlockActionHandler
import com.slack.api.bolt.request.builtin.BlockActionRequest
import com.slack.api.app_backend.interactive_components.response.ActionResponse
import com.slack.api.bolt.response.Response
import com.slack.api.bolt.context.builtin.ActionContext
object Handler : BlockActionHandler {
override fun apply(req: BlockActionRequest,
context: ActionContext): Response {
val response = ActionResponse
.builder()
.deleteOriginal(true)
.replaceOriginal(true)
.responseType("ephemeral")
.blocks(listOf())
.text("")
.build()
context.respond(response)
return context.ack()
}
}
If you are using Python and Flask the following code should work when you respond to a button click in the ephemeral message:
from flask import jsonify
response = jsonify({
'response_type': 'ephemeral',
'text': '',
'replace_original': 'true',
'delete_original':'true'
})
return make_response(response, 200)

MailCore2 - Fetch message content without downloading images or attachments?

I want to provide a preview of a user's messages, but I don't wish to download attachments just in order to do that.
The information I need is:
Subject
Date
Sender information (display name if available, email address)
Plain-text message
By calling the method fetchMessagesByNumberOperationWithFolder: with request kind MCOIMAPMessagesRequestKindHeaders, I get the date and subject, but it's very slow to return if any of the messages have attachments. By calling it with request kind MCOIMAPMessagesRequestKindUid, it returns very quickly, with just the Uid (and the current date, as a placeholder). From there, I still need to get the subject, the date and the sender.
Now I'm still trying to get this information, while avoiding downloading message attachments.
Calling fetchParsedMessageOperationWithFolder: or fetchMessageOperationWithFolder: both download the message with attachments, and are thus very slow to return.
Because fetchMessageOperationWithFolder: completes with a NSData object, checking the size of the given object reveals it to be the size of a regular message, plus its' attachment.
What can I do to get the information I need, without downloading any attachments?
EDIT: Calling requiredPartsForRendering could give me the content of the email, but in order to retrieve the Date and sender information, I'd still have to have a request kind of MCOIMAPMessagesRequestKindHeaders, which would download the attachment.
So to clarify:
I want to get the following information from an IMAP email, without downloading the email attachments:
Subject
Date
Sender information (display name if available, email address)
Plain-text message
The following methods should be helpful to you:
-[MCOAbstractMessage requiredPartsForRendering] will return the message parts that you needs to fetch to be able to show the text content of the message.
-[MCOIMAPSession fetchMessageAttachmentOperationWithFolder:uid:partID:encoding:] will help you fetch each of those parts.
-[MCOIMAPMessage htmlRenderingWithFolder:delegate:] will return the rendered content as HTML (or nil if you don't provide all the content of the parts through the delegate.
-[NSString mco_flattenHTML] is also useful if you'd like to convert the HTML to a unformatted string.

What is the purpose of each parameter in a MailChimp unsubscribe URL?

In the following URL, that unsubscribes a user from a list:
http://xxxxx.us2.list-manage.com/unsubscribe?
u=87f537bccxx35e53a1890e0d9&
id=40dcxx6cd6&
e=c4aaxx1dd6&
c=9a6xx11963
What does each parameter do?
Today I had to learn what each nondescript parameter means so that I could generate URLs for each email I send through Mandrill. It wasn't that easy to discover their meaning, but here are the one's I've come up with and how I found out what they are.
http://xxxxx.us2.list-manage.com/unsubscribe?
u=87f537bccxx35e53a1890e0d9&
id=40dcxx6cd6&
e=c4aaxx1dd6&
c=9a6xx11963
URL format:
Protocol can be http or https.
Your username comes next
In the example, us2 is the MailChimp datacenter where your account resides. That's different for each account. (https://apidocs.mailchimp.com/api/2.0/) It's good practice to specify a DC even though the documentation says that it isn't required. Specifying it will cut down on unnecessary latency.
list-manage[n].com can work with or without the number at the end of the domain, or it can have a 1 or a 2. Changing that doesn't appear to matter, but I think it has something to do with their load balancing.
/unsubscribe can also be /subscribe or /profile (The latter appears to be dependent on the "e" parameter. (See below) When you don't specify it, it states, "List member profiles cannot be updated from test campaigns or archive pages" and if you specify an invalid value, you get an error page.
u Is a unique identifier for your account. Every list on your account uses it. (See http://kb.mailchimp.com/lists/signup-forms/find-the-unsubscribe-link-for-your-list for how you can view the various URLs for your account.)
id is your list ID
e is the euid as documented on https://apidocs.mailchimp.com/api/2.0/lists/subscribe.php
c I haven't seen this one yet, but my guess is that it's the campaign ID.
Also, when you wish to prefill subscribe and unsubscribe forms, you can use the following GET params.
EMAIL Allows you to enter the subscriber's email
MERGE1 Allows you to enter the subscriber's first name
MERGE2 Allows you to enter the subscriber's last name

Getting Invalid Token response when trying to create D2L user account

I am trying to modify a D2L database from within a 3rd party application using their Valence API. I've gotten some operations to work but am stuck trying to create a new user account. I have been told that the account I am working under is authorized to do this.
I’ve defined a JSON object to hold the values I want:
{
"OrgDefinedId": "XX000TEST",
"FirstName": "Tom",
"MiddleName": "",
"LastName": "Foolery",
"ExternalEmail": "tom#something.com",
"UserName": "Tom.Foolery",
"RoleId": "78",
"IsActive": "true",
"SendCreationEmail": "false"
}
I copied the above text to the HTTP post buffer and then called the following link:
/d2l/api/lp/1.0/users/?
The parameter string contains the IDs and signatures (x_a, x_b, etc) as specified in the Valence docs. I assume the authorization values are correct, since I'm getting correct results when using the same algorithms on other Valence queries.
Any suggestions on how to get past the "Invalid Token" message would be appreciated.
--stein
If you're getting a 403 "Invalid Token" message then you are not, for some reason, forming your x_a, x_b, x_c, or x_d authentication tokens correctly. Common problems we have seen in the past are:
Trying to re-use x_c and or x_d signatures generated for one API call with another
Getting the tokens swapped around: x_a is App ID, x_c is App Sig, x_b is User ID, and x_d is User Sig
Generating the signatures using the wrong HTTP method (the method is one of the components of the base string for the signatures)
Not using all upper case letters for the HTTP method in the base string (the component should be GET not get)
Not using all lower case letters for the API route in the base string, or including incorrect characters: for example, in your question, you seem to imply that you're passing in the ? as a part of the route; you shouldn't do this. In this case, your base string for creating the URL should be POST&/d2l/api/lp/1.0/users/&1234567 where 1234567 should be replaced with the timestamp you generate and also pass in x_t
Using the API route with API version component provided, but when calling, using another version component (i.e. generating with /d2l/api/lp/1.0/... but calling with /d2l/api/lp/1.1/...)
Using an incorrect/mismatching timestamp value in the base string (the timestamp you use for the basestring should be in seconds, and be the same stamp as the x_t value)
While calls previously worked, suddenly none of the calls work with a 403 invalid token result: the user tokens could have expired and you need to re-authenticate the user
While calls previously worked against a test instance, when you try moving to a different LMS (prod instance for example) the calls don't work: perhaps the App ID/Key pair hasn't shown up on this new LMS, or you're trying to use the user ID/Key pair from one LMS to generate signatures on a different LMS
Also, notice that your JSON object is strictly not correctly formed: the IsActive and SendCreationEmail properties should have values of true and false respectively, not "true" and "false", although it's possible that the LMS parser on the server side will be forgiving about that.
If none of these points assist you, please feel free to open an issue in our issue tracker, or contact our Valence support email address, and we can try to help you out through this issue.
NOTE Please note that invalid tokens will throw you back a 403 (but the message will be "Invalid Token" or "Expired Token" or similar). If your tokens are correctly generated, but your calling user context is not allowed to create a user, then you'll also get a 403, but this time the message will be "Not Permitted" or "Not Authorized" or similar. Make sure you double check what sort of 403 you're getting back.
In this particular case, the permissions around creating a user are a bit tricky; not only must you have a permission to create a user, you must also have permission to modify the properties that you will be passing into the API in the CreateUserData structure (OrgDefinedId, Email, and so on), and you must also be able to see all those fields in the User Information Privacy settings, and you must have permission to enroll the user role you have provided at the organization level... those last two bits have tripped up some of our clients in the past.

IMAP FETCH Subject

Using IMAP via telnet, I want to be able to extract the subject from the specific given email. Now I know that the fetch command is responsible for getting data from an email.
My question is, how do I get the subject header specifically, without using a call to BODY[HEADER.FIELDS (SUBJECT)] (which will, in the eyes of the server, 'open the email' and thus set the /seen flag, which is what I don't want to occur)?
I understand FETCH FULL returns the full header, which contains the subject but it's a nightmare to parse through and could be riddled with unseen pitfalls if I manually parse it. How would I get the server to give me just the subject from the header?
I discovered the answer:
BODY.PEEK[HEADER.FIELDS (SUBJECT)]
.PEEK tells it not open it (so /seen isn't set).
Besides BODY.PEEK, you could fetch ENVELOPE, which gives you a parsed summary of much of the message metadata.
"a1 FETCH 1:* (FLAGS BODY[HEADER.FIELDS (SUBJECT DATE FROM)])\r\n"

Resources