{"action":"created","comment":{"url":"https://api.github.com/repos/apache/couchdb-fabric/pulls/comments/41984239","id":41984239,"diff_hunk":"@@ -0,0 +1,333 @@\n+%% @author gilv\n+%% @doc @todo Add description to fabric_swift_handler.\n+\n+\n+-module(fabric_attachments_handler).\n+\n+-include_lib(\"fabric/include/fabric.hrl\").\n+-include_lib(\"couch/include/couch_db.hrl\").\n+\n+-export([inline_att_store/2, inline_att_handler/3, container_handler/2, normal_att_store/5, externalize_att/1]).\n+\n+\n+%% ====================================================================\n+%% External API functions. I try to keep them as general as possible, \n+%% without correlation to specific object store that might be internally used.\n+%% ====================================================================\n+\n+normal_att_store(FileName,Db,ContentLen,MimeType,Req) ->\n+    ContainerName = container_name(Db),\n+    couch_log:debug(\"Standard attachment handler\",[]),\n+    couch_log:debug(\"Going to store ~p of length is ~p,  ~p in the container: ~n\",[FileName,ContentLen,ContainerName]),\n+    %Bad implementation - no chunk reader. All kept in the memory. Should be fixed.\n+    Data = couch_httpd:recv(Req, ContentLen),\n+    case swift_put_object(ContainerName, FileName, MimeType, [], Data) of\n+        {ok,{201,NewUrl}} ->\n+            couch_log:debug(\"Created. Swift response code is 201 ~n\",[]),\n+            {ObjectSize,EtagMD5} = swift_head_object(ContainerName, FileName),\n+            {unicode:characters_to_binary(NewUrl, unicode, utf8), ObjectSize, EtagMD5};\n+        {_,{Code,_}} ->\n+            couch_log:debug(\"Swift response code is ~p~n\",[]),\n+            {Data, -1}\n+    end.\n+\n+inline_att_store(Db, Docs) ->\n+    DbName = container_name(Db),\n+    couch_log:debug(\"Store inline base64 encoded attachment ~n\",[]),\n+    if \n+        is_list(Docs)  ->\n+            couch_log:debug(\"Going to handle document list\",[]),\n+            DocsArray = Docs;\n+        true ->\n+            couch_log:debug(\"Going to handle single document\",[]),\n+            DocsArray = [Docs]\n+    end,\n+    [#doc{atts=Atts0} = Doc | Rest] = DocsArray,\n+    if \n+        length(Atts0) > 0 ->\n+            couch_log:debug(\"Att length is larger than 0\",[]),\n+            Atts = [att_processor(DbName,Att) || Att <- Atts0],\n+            if \n+                is_list(Docs)  ->\n+                    [Doc#doc{atts = Atts} | Rest];\n+            true ->\n+                Doc#doc{atts = Atts}\n+            end;\n+        true ->\n+            couch_log:debug(\"No attachments to handle\",[]),\n+            Docs\n+    end.\n+\n+inline_att_handler(get, Db, Att) ->\n+    DbName = container_name(Db),\n+    couch_log:debug(\"Retrieve attachment\",[]),\n+    [Data,Name,AttLen,AttLenUser] = couch_att:fetch([data, name,att_len, att_external_size],Att),\n+    couch_log:debug(\"Going to retrieve ~p. DB length is: ~p, stored length: ~p~n\",[Name, AttLen, AttLenUser]),\n+    NewData = swift_get_object(DbName, Name),\n+    NewAtt = couch_att:store([{data, NewData},{att_len,AttLenUser},{disk_len,AttLenUser}], Att),\n+    NewAtt;\n+\n+inline_att_handler(delete,Db, Att) ->\n+    DbName = container_name(Db),\n+    couch_log:debug(\"Delete attachment ~p~n\",[]).\n+\n+container_handler(create,DbName) ->\n+    couch_log:debug(\"Create container ~p~n\",[DbName]),\n+    case swift_create_container(DbName) of\n+        {ok,{201,Container}} ->\n+            couch_log:debug(\"Container ~p created succesfully ~n\",[Container]),\n+            {ok,Container};\n+        {error,_} ->\n+            couch_log:debug(\"Container ~p creation failed ~n\",[DbName]),\n+            {error,DbName}\n+    end;\n+container_handler(get,DbName) ->\n+    couch_log:debug(\"Get container ~p~n\",[DbName]);\n+container_handler(delete,DbName)->\n+    couch_log:debug(\"Delete container ~p~n\",[DbName]),\n+    swift_delete_container(DbName).\n+\n+externalize_att(Db) ->\n+    Res = config:get(\"swift\",\"attachments_offload\",\"false\"),\n+    Res.\n+\n+%% ====================================================================\n+%% Internal general functions\n+%% ====================================================================\n+\n+container_name(Db) ->\n+    Suffix = list_to_binary(mem3:shard_suffix(Db)),\n+    DbNameSuffix = erlang:iolist_to_binary([fabric:dbname(Db), Suffix]),","path":"src/fabric_attachments_handler.erl","position":100,"original_position":100,"commit_id":"d029d3ae64a047131b022f52126a8bf7f765f823","original_commit_id":"d029d3ae64a047131b022f52126a8bf7f765f823","user":{"login":"kxepal","id":785983,"avatar_url":"https://avatars.githubusercontent.com/u/785983?v=3","gravatar_id":"","url":"https://api.github.com/users/kxepal","html_url":"https://github.com/kxepal","followers_url":"https://api.github.com/users/kxepal/followers","following_url":"https://api.github.com/users/kxepal/following{/other_user}","gists_url":"https://api.github.com/users/kxepal/gists{/gist_id}","starred_url":"https://api.github.com/users/kxepal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kxepal/subscriptions","organizations_url":"https://api.github.com/users/kxepal/orgs","repos_url":"https://api.github.com/users/kxepal/repos","events_url":"https://api.github.com/users/kxepal/events{/privacy}","received_events_url":"https://api.github.com/users/kxepal/received_events","type":"User","site_admin":false},"body":"You can actually concat binaries instead or not use list_to_binary cast above.","created_at":"2015-10-14T11:56:04Z","updated_at":"2015-10-14T11:56:04Z","html_url":"https://github.com/apache/couchdb-fabric/pull/33#discussion_r41984239","pull_request_url":"https://api.github.com/repos/apache/couchdb-fabric/pulls/33","_links":{"self":{"href":"https://api.github.com/repos/apache/couchdb-fabric/pulls/comments/41984239"},"html":{"href":"https://github.com/apache/couchdb-fabric/pull/33#discussion_r41984239"},"pull_request":{"href":"https://api.github.com/repos/apache/couchdb-fabric/pulls/33"}}},"pull_request":{"url":"https://api.github.com/repos/apache/couchdb-fabric/pulls/33","id":47644990,"html_url":"https://github.com/apache/couchdb-fabric/pull/33","diff_url":"https://github.com/apache/couchdb-fabric/pull/33.diff","patch_url":"https://github.com/apache/couchdb-fabric/pull/33.patch","issue_url":"https://api.github.com/repos/apache/couchdb-fabric/issues/33","number":33,"state":"open","locked":false,"title":"COUCHDB-769: Store attachments in the external storage. ","user":{"login":"gilv","id":6321715,"avatar_url":"https://avatars.githubusercontent.com/u/6321715?v=3","gravatar_id":"","url":"https://api.github.com/users/gilv","html_url":"https://github.com/gilv","followers_url":"https://api.github.com/users/gilv/followers","following_url":"https://api.github.com/users/gilv/following{/other_user}","gists_url":"https://api.github.com/users/gilv/gists{/gist_id}","starred_url":"https://api.github.com/users/gilv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gilv/subscriptions","organizations_url":"https://api.github.com/users/gilv/orgs","repos_url":"https://api.github.com/users/gilv/repos","events_url":"https://api.github.com/users/gilv/events{/privacy}","received_events_url":"https://api.github.com/users/gilv/received_events","type":"User","site_admin":false},"body":"Initial implementation that allows CouchDB to store attachments outside of the database file.\r\nThis implementation supports OpenStack Swift and SoftLayer Object store","created_at":"2015-10-14T11:04:46Z","updated_at":"2015-10-14T11:56:04Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c1b417a6b09eb5de14bc25ca2e9224824d232bd7","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/apache/couchdb-fabric/pulls/33/commits","review_comments_url":"https://api.github.com/repos/apache/couchdb-fabric/pulls/33/comments","review_comment_url":"https://api.github.com/repos/apache/couchdb-fabric/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/couchdb-fabric/issues/33/comments","statuses_url":"https://api.github.com/repos/apache/couchdb-fabric/statuses/d029d3ae64a047131b022f52126a8bf7f765f823","head":{"label":"gilv:store_attachments_external","ref":"store_attachments_external","sha":"d029d3ae64a047131b022f52126a8bf7f765f823","user":{"login":"gilv","id":6321715,"avatar_url":"https://avatars.githubusercontent.com/u/6321715?v=3","gravatar_id":"","url":"https://api.github.com/users/gilv","html_url":"https://github.com/gilv","followers_url":"https://api.github.com/users/gilv/followers","following_url":"https://api.github.com/users/gilv/following{/other_user}","gists_url":"https://api.github.com/users/gilv/gists{/gist_id}","starred_url":"https://api.github.com/users/gilv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gilv/subscriptions","organizations_url":"https://api.github.com/users/gilv/orgs","repos_url":"https://api.github.com/users/gilv/repos","events_url":"https://api.github.com/users/gilv/events{/privacy}","received_events_url":"https://api.github.com/users/gilv/received_events","type":"User","site_admin":false},"repo":{"id":44170094,"name":"couchdb-fabric","full_name":"gilv/couchdb-fabric","owner":{"login":"gilv","id":6321715,"avatar_url":"https://avatars.githubusercontent.com/u/6321715?v=3","gravatar_id":"","url":"https://api.github.com/users/gilv","html_url":"https://github.com/gilv","followers_url":"https://api.github.com/users/gilv/followers","following_url":"https://api.github.com/users/gilv/following{/other_user}","gists_url":"https://api.github.com/users/gilv/gists{/gist_id}","starred_url":"https://api.github.com/users/gilv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gilv/subscriptions","organizations_url":"https://api.github.com/users/gilv/orgs","repos_url":"https://api.github.com/users/gilv/repos","events_url":"https://api.github.com/users/gilv/events{/privacy}","received_events_url":"https://api.github.com/users/gilv/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gilv/couchdb-fabric","description":"Mirror of Apache CouchDB","fork":true,"url":"https://api.github.com/repos/gilv/couchdb-fabric","forks_url":"https://api.github.com/repos/gilv/couchdb-fabric/forks","keys_url":"https://api.github.com/repos/gilv/couchdb-fabric/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gilv/couchdb-fabric/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gilv/couchdb-fabric/teams","hooks_url":"https://api.github.com/repos/gilv/couchdb-fabric/hooks","issue_events_url":"https://api.github.com/repos/gilv/couchdb-fabric/issues/events{/number}","events_url":"https://api.github.com/repos/gilv/couchdb-fabric/events","assignees_url":"https://api.github.com/repos/gilv/couchdb-fabric/assignees{/user}","branches_url":"https://api.github.com/repos/gilv/couchdb-fabric/branches{/branch}","tags_url":"https://api.github.com/repos/gilv/couchdb-fabric/tags","blobs_url":"https://api.github.com/repos/gilv/couchdb-fabric/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gilv/couchdb-fabric/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gilv/couchdb-fabric/git/refs{/sha}","trees_url":"https://api.github.com/repos/gilv/couchdb-fabric/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gilv/couchdb-fabric/statuses/{sha}","languages_url":"https://api.github.com/repos/gilv/couchdb-fabric/languages","stargazers_url":"https://api.github.com/repos/gilv/couchdb-fabric/stargazers","contributors_url":"https://api.github.com/repos/gilv/couchdb-fabric/contributors","subscribers_url":"https://api.github.com/repos/gilv/couchdb-fabric/subscribers","subscription_url":"https://api.github.com/repos/gilv/couchdb-fabric/subscription","commits_url":"https://api.github.com/repos/gilv/couchdb-fabric/commits{/sha}","git_commits_url":"https://api.github.com/repos/gilv/couchdb-fabric/git/commits{/sha}","comments_url":"https://api.github.com/repos/gilv/couchdb-fabric/comments{/number}","issue_comment_url":"https://api.github.com/repos/gilv/couchdb-fabric/issues/comments{/number}","contents_url":"https://api.github.com/repos/gilv/couchdb-fabric/contents/{+path}","compare_url":"https://api.github.com/repos/gilv/couchdb-fabric/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gilv/couchdb-fabric/merges","archive_url":"https://api.github.com/repos/gilv/couchdb-fabric/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gilv/couchdb-fabric/downloads","issues_url":"https://api.github.com/repos/gilv/couchdb-fabric/issues{/number}","pulls_url":"https://api.github.com/repos/gilv/couchdb-fabric/pulls{/number}","milestones_url":"https://api.github.com/repos/gilv/couchdb-fabric/milestones{/number}","notifications_url":"https://api.github.com/repos/gilv/couchdb-fabric/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gilv/couchdb-fabric/labels{/name}","releases_url":"https://api.github.com/repos/gilv/couchdb-fabric/releases{/id}","created_at":"2015-10-13T10:55:10Z","updated_at":"2015-10-13T10:55:11Z","pushed_at":"2015-10-14T11:02:56Z","git_url":"git://github.com/gilv/couchdb-fabric.git","ssh_url":"git@github.com:gilv/couchdb-fabric.git","clone_url":"https://github.com/gilv/couchdb-fabric.git","svn_url":"https://github.com/gilv/couchdb-fabric","homepage":null,"size":1381,"stargazers_count":0,"watchers_count":0,"language":"Erlang","has_issues":false,"has_downloads":true,"has_wiki":false,"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":"apache:master","ref":"master","sha":"a6d07a1cbf9e7ea07f171f223710891ac7c627b4","user":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":15993611,"name":"couchdb-fabric","full_name":"apache/couchdb-fabric","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/couchdb-fabric","description":"Mirror of Apache CouchDB","fork":false,"url":"https://api.github.com/repos/apache/couchdb-fabric","forks_url":"https://api.github.com/repos/apache/couchdb-fabric/forks","keys_url":"https://api.github.com/repos/apache/couchdb-fabric/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/couchdb-fabric/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/couchdb-fabric/teams","hooks_url":"https://api.github.com/repos/apache/couchdb-fabric/hooks","issue_events_url":"https://api.github.com/repos/apache/couchdb-fabric/issues/events{/number}","events_url":"https://api.github.com/repos/apache/couchdb-fabric/events","assignees_url":"https://api.github.com/repos/apache/couchdb-fabric/assignees{/user}","branches_url":"https://api.github.com/repos/apache/couchdb-fabric/branches{/branch}","tags_url":"https://api.github.com/repos/apache/couchdb-fabric/tags","blobs_url":"https://api.github.com/repos/apache/couchdb-fabric/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/couchdb-fabric/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/couchdb-fabric/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/couchdb-fabric/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/couchdb-fabric/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/couchdb-fabric/languages","stargazers_url":"https://api.github.com/repos/apache/couchdb-fabric/stargazers","contributors_url":"https://api.github.com/repos/apache/couchdb-fabric/contributors","subscribers_url":"https://api.github.com/repos/apache/couchdb-fabric/subscribers","subscription_url":"https://api.github.com/repos/apache/couchdb-fabric/subscription","commits_url":"https://api.github.com/repos/apache/couchdb-fabric/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/couchdb-fabric/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/couchdb-fabric/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/couchdb-fabric/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/couchdb-fabric/contents/{+path}","compare_url":"https://api.github.com/repos/apache/couchdb-fabric/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/couchdb-fabric/merges","archive_url":"https://api.github.com/repos/apache/couchdb-fabric/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/couchdb-fabric/downloads","issues_url":"https://api.github.com/repos/apache/couchdb-fabric/issues{/number}","pulls_url":"https://api.github.com/repos/apache/couchdb-fabric/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/couchdb-fabric/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/couchdb-fabric/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/couchdb-fabric/labels{/name}","releases_url":"https://api.github.com/repos/apache/couchdb-fabric/releases{/id}","created_at":"2014-01-17T08:00:08Z","updated_at":"2015-04-24T00:21:25Z","pushed_at":"2015-10-14T11:04:46Z","git_url":"git://github.com/apache/couchdb-fabric.git","ssh_url":"git@github.com:apache/couchdb-fabric.git","clone_url":"https://github.com/apache/couchdb-fabric.git","svn_url":"https://github.com/apache/couchdb-fabric","homepage":null,"size":1381,"stargazers_count":5,"watchers_count":5,"language":"Erlang","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":13,"mirror_url":"git://git.apache.org/couchdb-fabric.git","open_issues_count":4,"forks":13,"open_issues":4,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/couchdb-fabric/pulls/33"},"html":{"href":"https://github.com/apache/couchdb-fabric/pull/33"},"issue":{"href":"https://api.github.com/repos/apache/couchdb-fabric/issues/33"},"comments":{"href":"https://api.github.com/repos/apache/couchdb-fabric/issues/33/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/couchdb-fabric/pulls/33/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/couchdb-fabric/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/couchdb-fabric/pulls/33/commits"},"statuses":{"href":"https://api.github.com/repos/apache/couchdb-fabric/statuses/d029d3ae64a047131b022f52126a8bf7f765f823"}}}}