{"action":"opened","number":178,"pull_request":{"url":"https://api.github.com/repos/gleber/erlcloud/pulls/178","id":39543384,"html_url":"https://github.com/gleber/erlcloud/pull/178","diff_url":"https://github.com/gleber/erlcloud/pull/178.diff","patch_url":"https://github.com/gleber/erlcloud/pull/178.patch","issue_url":"https://api.github.com/repos/gleber/erlcloud/issues/178","number":178,"state":"open","locked":false,"title":"erlcloud_ses: support (most of) the rest of the AWS SES API","user":{"login":"nalundgaard","id":1851445,"avatar_url":"https://avatars.githubusercontent.com/u/1851445?v=3","gravatar_id":"","url":"https://api.github.com/users/nalundgaard","html_url":"https://github.com/nalundgaard","followers_url":"https://api.github.com/users/nalundgaard/followers","following_url":"https://api.github.com/users/nalundgaard/following{/other_user}","gists_url":"https://api.github.com/users/nalundgaard/gists{/gist_id}","starred_url":"https://api.github.com/users/nalundgaard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nalundgaard/subscriptions","organizations_url":"https://api.github.com/users/nalundgaard/orgs","repos_url":"https://api.github.com/users/nalundgaard/repos","events_url":"https://api.github.com/users/nalundgaard/events{/privacy}","received_events_url":"https://api.github.com/users/nalundgaard/received_events","type":"User","site_admin":false},"body":"Hi,\r\n\r\nI work for [Alert Logic](https://github.com/alertlogic), and we use a [fork](https://github.com/alertlogic/erlcloud) of erlcloud for some of our work. I have been working with SES, and the need has arisen for us to leverage some SES functionality that isn't exposed by the `erlcloud_ses` module.\r\n\r\nAs a result, I have added most of the missing functions for the SES API to `erlcloud_ses`. The remaining methods I have not implemented were not needed by me, and I wasn't sure I wanted to mess with handling the url-encoded JSON mess with the SES `Policy` stuff. So, there are a few unimplemented methods that are deprecated by AWS, all of the Policies actions, and SendRawEmail.\r\n\r\nThese can easily be added in the future. I've opted not to implement these changes as `erlcloud_ses2` because the changes are generally backwards-compatible, and the only non-backwards-compatible thing that I know of (parsing the send_email response) is likely a change to a section of the result that I **hope** no one was using (a big naked XML string). More on that below.\r\n\r\nI've endeavored to adhere to the style of the existing module and use patterns from elsewhere in erlcloud as much as possible. I thought I would submit this to you guys for your feedback. Thanks for looking!\r\n\r\n## Commit notes\r\n\r\nMethods not implemented:\r\n  * DeleteIdentityPolicy\r\n  * DeleteVerifiedEmailAddress (deprecated; use DeleteIdentity)\r\n  * GetIdentityPolicies\r\n  * ListIdentityPolicies\r\n  * ListVerifiedEmailAddresses (deprecated; use ListIdentities)\r\n  * PutIdentityPolicy\r\n  * SendRawEmail\r\n  * VerifyEmailAddress (deprecated; use VerifyEmailIdentity)\r\n\r\nAll other methods have been implemented. I have tested each method against the SES API using my own AWS account. This is backwards-compatible with the existing function (SendEmail was the only one), only I have added XML parsing to all methods—so, now, send_email returns `{ok, [{message_id, string()}]}` instead of a tuple of `ok` and a giant, naked XML body. If any client used this part of the response (e.g., some downstream parsing), this change will break that functionality.\r\n\r\nOther functions in the API generally return proplists, with title case parameters from AWS converted to erlang atoms where possible. Other values are converted to strings, integers, floats, and booleans as appropriate for the corresponding values in the AWS response.\r\n\r\nI've also added the ability to pass single items to lists. This means that, for example, in the `bcc_addresses` field of `To`, I can pass a single email address. The same is true for any function that expects a list of email addresses, identities, etc.\r\n\r\nFinally, errors: now, `erlcloud_ses` returns parsed errors when AWS responds with XML. It also returns erlang errors when invalid parameters (that cannot be encoded) are passed to it. Here are some examples:\r\n\r\n```erlang\r\nerlcloud_ses:set_identity_feedback_forwarding_enabled(\"email@example.com\", false).\r\n{error,{erlcloud_ses,{invalid_parameter_value,\"Feedback notification topic is not set.\"}}}\r\n```\r\n```erlang\r\nerlcloud_ses:set_identity_dkim_enabled(\"example.com\", trye).\r\n** exception error: {erlcloud_ses,{invalid_parameter,{dkim_enabled,trye}}}\r\n```","created_at":"2015-07-09T02:42:39Z","updated_at":"2015-07-09T02:42:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/gleber/erlcloud/pulls/178/commits","review_comments_url":"https://api.github.com/repos/gleber/erlcloud/pulls/178/comments","review_comment_url":"https://api.github.com/repos/gleber/erlcloud/pulls/comments{/number}","comments_url":"https://api.github.com/repos/gleber/erlcloud/issues/178/comments","statuses_url":"https://api.github.com/repos/gleber/erlcloud/statuses/c2d0d8b42d36dbc187d4fb458eedf44b47d8308e","head":{"label":"nalundgaard:improve_erlcloud_ses","ref":"improve_erlcloud_ses","sha":"c2d0d8b42d36dbc187d4fb458eedf44b47d8308e","user":{"login":"nalundgaard","id":1851445,"avatar_url":"https://avatars.githubusercontent.com/u/1851445?v=3","gravatar_id":"","url":"https://api.github.com/users/nalundgaard","html_url":"https://github.com/nalundgaard","followers_url":"https://api.github.com/users/nalundgaard/followers","following_url":"https://api.github.com/users/nalundgaard/following{/other_user}","gists_url":"https://api.github.com/users/nalundgaard/gists{/gist_id}","starred_url":"https://api.github.com/users/nalundgaard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nalundgaard/subscriptions","organizations_url":"https://api.github.com/users/nalundgaard/orgs","repos_url":"https://api.github.com/users/nalundgaard/repos","events_url":"https://api.github.com/users/nalundgaard/events{/privacy}","received_events_url":"https://api.github.com/users/nalundgaard/received_events","type":"User","site_admin":false},"repo":{"id":37937343,"name":"erlcloud","full_name":"nalundgaard/erlcloud","owner":{"login":"nalundgaard","id":1851445,"avatar_url":"https://avatars.githubusercontent.com/u/1851445?v=3","gravatar_id":"","url":"https://api.github.com/users/nalundgaard","html_url":"https://github.com/nalundgaard","followers_url":"https://api.github.com/users/nalundgaard/followers","following_url":"https://api.github.com/users/nalundgaard/following{/other_user}","gists_url":"https://api.github.com/users/nalundgaard/gists{/gist_id}","starred_url":"https://api.github.com/users/nalundgaard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nalundgaard/subscriptions","organizations_url":"https://api.github.com/users/nalundgaard/orgs","repos_url":"https://api.github.com/users/nalundgaard/repos","events_url":"https://api.github.com/users/nalundgaard/events{/privacy}","received_events_url":"https://api.github.com/users/nalundgaard/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/nalundgaard/erlcloud","description":"Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB)","fork":true,"url":"https://api.github.com/repos/nalundgaard/erlcloud","forks_url":"https://api.github.com/repos/nalundgaard/erlcloud/forks","keys_url":"https://api.github.com/repos/nalundgaard/erlcloud/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nalundgaard/erlcloud/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nalundgaard/erlcloud/teams","hooks_url":"https://api.github.com/repos/nalundgaard/erlcloud/hooks","issue_events_url":"https://api.github.com/repos/nalundgaard/erlcloud/issues/events{/number}","events_url":"https://api.github.com/repos/nalundgaard/erlcloud/events","assignees_url":"https://api.github.com/repos/nalundgaard/erlcloud/assignees{/user}","branches_url":"https://api.github.com/repos/nalundgaard/erlcloud/branches{/branch}","tags_url":"https://api.github.com/repos/nalundgaard/erlcloud/tags","blobs_url":"https://api.github.com/repos/nalundgaard/erlcloud/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nalundgaard/erlcloud/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nalundgaard/erlcloud/git/refs{/sha}","trees_url":"https://api.github.com/repos/nalundgaard/erlcloud/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nalundgaard/erlcloud/statuses/{sha}","languages_url":"https://api.github.com/repos/nalundgaard/erlcloud/languages","stargazers_url":"https://api.github.com/repos/nalundgaard/erlcloud/stargazers","contributors_url":"https://api.github.com/repos/nalundgaard/erlcloud/contributors","subscribers_url":"https://api.github.com/repos/nalundgaard/erlcloud/subscribers","subscription_url":"https://api.github.com/repos/nalundgaard/erlcloud/subscription","commits_url":"https://api.github.com/repos/nalundgaard/erlcloud/commits{/sha}","git_commits_url":"https://api.github.com/repos/nalundgaard/erlcloud/git/commits{/sha}","comments_url":"https://api.github.com/repos/nalundgaard/erlcloud/comments{/number}","issue_comment_url":"https://api.github.com/repos/nalundgaard/erlcloud/issues/comments{/number}","contents_url":"https://api.github.com/repos/nalundgaard/erlcloud/contents/{+path}","compare_url":"https://api.github.com/repos/nalundgaard/erlcloud/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nalundgaard/erlcloud/merges","archive_url":"https://api.github.com/repos/nalundgaard/erlcloud/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nalundgaard/erlcloud/downloads","issues_url":"https://api.github.com/repos/nalundgaard/erlcloud/issues{/number}","pulls_url":"https://api.github.com/repos/nalundgaard/erlcloud/pulls{/number}","milestones_url":"https://api.github.com/repos/nalundgaard/erlcloud/milestones{/number}","notifications_url":"https://api.github.com/repos/nalundgaard/erlcloud/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nalundgaard/erlcloud/labels{/name}","releases_url":"https://api.github.com/repos/nalundgaard/erlcloud/releases{/id}","created_at":"2015-06-23T18:31:44Z","updated_at":"2015-06-23T18:31:44Z","pushed_at":"2015-07-09T02:31:04Z","git_url":"git://github.com/nalundgaard/erlcloud.git","ssh_url":"git@github.com:nalundgaard/erlcloud.git","clone_url":"https://github.com/nalundgaard/erlcloud.git","svn_url":"https://github.com/nalundgaard/erlcloud","homepage":"","size":2852,"stargazers_count":0,"watchers_count":0,"language":"Erlang","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"gleber:master","ref":"master","sha":"6b2e77c59045eb4debf126aae60f1b6c90d1f080","user":{"login":"gleber","id":33185,"avatar_url":"https://avatars.githubusercontent.com/u/33185?v=3","gravatar_id":"","url":"https://api.github.com/users/gleber","html_url":"https://github.com/gleber","followers_url":"https://api.github.com/users/gleber/followers","following_url":"https://api.github.com/users/gleber/following{/other_user}","gists_url":"https://api.github.com/users/gleber/gists{/gist_id}","starred_url":"https://api.github.com/users/gleber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gleber/subscriptions","organizations_url":"https://api.github.com/users/gleber/orgs","repos_url":"https://api.github.com/users/gleber/repos","events_url":"https://api.github.com/users/gleber/events{/privacy}","received_events_url":"https://api.github.com/users/gleber/received_events","type":"User","site_admin":false},"repo":{"id":988103,"name":"erlcloud","full_name":"gleber/erlcloud","owner":{"login":"gleber","id":33185,"avatar_url":"https://avatars.githubusercontent.com/u/33185?v=3","gravatar_id":"","url":"https://api.github.com/users/gleber","html_url":"https://github.com/gleber","followers_url":"https://api.github.com/users/gleber/followers","following_url":"https://api.github.com/users/gleber/following{/other_user}","gists_url":"https://api.github.com/users/gleber/gists{/gist_id}","starred_url":"https://api.github.com/users/gleber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gleber/subscriptions","organizations_url":"https://api.github.com/users/gleber/orgs","repos_url":"https://api.github.com/users/gleber/repos","events_url":"https://api.github.com/users/gleber/events{/privacy}","received_events_url":"https://api.github.com/users/gleber/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gleber/erlcloud","description":"Cloud Computing library for erlang (Amazon EC2, S3, SQS, SimpleDB, Mechanical Turk, ELB)","fork":false,"url":"https://api.github.com/repos/gleber/erlcloud","forks_url":"https://api.github.com/repos/gleber/erlcloud/forks","keys_url":"https://api.github.com/repos/gleber/erlcloud/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gleber/erlcloud/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gleber/erlcloud/teams","hooks_url":"https://api.github.com/repos/gleber/erlcloud/hooks","issue_events_url":"https://api.github.com/repos/gleber/erlcloud/issues/events{/number}","events_url":"https://api.github.com/repos/gleber/erlcloud/events","assignees_url":"https://api.github.com/repos/gleber/erlcloud/assignees{/user}","branches_url":"https://api.github.com/repos/gleber/erlcloud/branches{/branch}","tags_url":"https://api.github.com/repos/gleber/erlcloud/tags","blobs_url":"https://api.github.com/repos/gleber/erlcloud/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gleber/erlcloud/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gleber/erlcloud/git/refs{/sha}","trees_url":"https://api.github.com/repos/gleber/erlcloud/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gleber/erlcloud/statuses/{sha}","languages_url":"https://api.github.com/repos/gleber/erlcloud/languages","stargazers_url":"https://api.github.com/repos/gleber/erlcloud/stargazers","contributors_url":"https://api.github.com/repos/gleber/erlcloud/contributors","subscribers_url":"https://api.github.com/repos/gleber/erlcloud/subscribers","subscription_url":"https://api.github.com/repos/gleber/erlcloud/subscription","commits_url":"https://api.github.com/repos/gleber/erlcloud/commits{/sha}","git_commits_url":"https://api.github.com/repos/gleber/erlcloud/git/commits{/sha}","comments_url":"https://api.github.com/repos/gleber/erlcloud/comments{/number}","issue_comment_url":"https://api.github.com/repos/gleber/erlcloud/issues/comments{/number}","contents_url":"https://api.github.com/repos/gleber/erlcloud/contents/{+path}","compare_url":"https://api.github.com/repos/gleber/erlcloud/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gleber/erlcloud/merges","archive_url":"https://api.github.com/repos/gleber/erlcloud/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gleber/erlcloud/downloads","issues_url":"https://api.github.com/repos/gleber/erlcloud/issues{/number}","pulls_url":"https://api.github.com/repos/gleber/erlcloud/pulls{/number}","milestones_url":"https://api.github.com/repos/gleber/erlcloud/milestones{/number}","notifications_url":"https://api.github.com/repos/gleber/erlcloud/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gleber/erlcloud/labels{/name}","releases_url":"https://api.github.com/repos/gleber/erlcloud/releases{/id}","created_at":"2010-10-14T20:32:41Z","updated_at":"2015-07-07T05:12:01Z","pushed_at":"2015-07-08T18:53:51Z","git_url":"git://github.com/gleber/erlcloud.git","ssh_url":"git@github.com:gleber/erlcloud.git","clone_url":"https://github.com/gleber/erlcloud.git","svn_url":"https://github.com/gleber/erlcloud","homepage":"","size":2997,"stargazers_count":289,"watchers_count":289,"language":"Erlang","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":163,"mirror_url":null,"open_issues_count":7,"forks":163,"open_issues":7,"watchers":289,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/gleber/erlcloud/pulls/178"},"html":{"href":"https://github.com/gleber/erlcloud/pull/178"},"issue":{"href":"https://api.github.com/repos/gleber/erlcloud/issues/178"},"comments":{"href":"https://api.github.com/repos/gleber/erlcloud/issues/178/comments"},"review_comments":{"href":"https://api.github.com/repos/gleber/erlcloud/pulls/178/comments"},"review_comment":{"href":"https://api.github.com/repos/gleber/erlcloud/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/gleber/erlcloud/pulls/178/commits"},"statuses":{"href":"https://api.github.com/repos/gleber/erlcloud/statuses/c2d0d8b42d36dbc187d4fb458eedf44b47d8308e"}},"merged":false,"mergeable":null,"mergeable_state":"unknown","merged_by":null,"comments":0,"review_comments":0,"commits":1,"additions":811,"deletions":29,"changed_files":1}}