{"action":"created","comment":{"url":"https://api.github.com/repos/otwcode/otwarchive/pulls/comments/65825646","id":65825646,"diff_hunk":"@@ -0,0 +1,175 @@\n+require 'spec_helper'\n+require 'api/api_helper'\n+\n+describe \"API BookmarksController\" do\n+  include ApiHelper\n+\n+  # Override is_archivist so all users are archivists from this point on\n+  class User < ActiveRecord::Base\n+    def is_archivist?\n+      true\n+    end\n+  end\n+\n+  bookmark = { id: \"123\",\n+               url: \"http://foo.com\",\n+               author: \"Thing\",\n+               title: \"Title Thing\",\n+               summary: \"<p>blah blah blah</p>\",\n+               fandom_string: \"Testing\",\n+               rating_string: \"General Audiences\",\n+               category_string: [\"M/M\"],\n+               relationship_string: \"Starsky/Hutch\",\n+               character_string: \"Starsky,hutch\",\n+               notes: \"<p>Notes</p>\",\n+               tag_string: \"youpi\",\n+               collection_names: \"\",\n+               private: \"0\",\n+               rec: \"0\" }\n+\n+  describe \"Valid API bookmark import\" do\n+    before do\n+      mock_external\n+      @user = create(:user)\n+    end\n+\n+    after do\n+      WebMock.reset!\n+    end\n+\n+    it \"should return 200 OK when all bookmarks are created\" do\n+      post \"/api/v1/bookmarks/import\",\n+           { archivist: @user.login,\n+             bookmarks: [ bookmark ]\n+           }.to_json,\n+           valid_headers\n+      assert_equal 200, response.status\n+    end\n+\n+    it \"should return 200 OK when no bookmarks are created\" do\n+      post \"/api/v1/bookmarks/import\",\n+           { archivist: @user.login,\n+             bookmarks: [ bookmark ]\n+           }.to_json,\n+           valid_headers\n+      assert_equal 200, response.status\n+    end\n+\n+    it \"should not create duplicate bookmarks for the same archivist and external URL\" do\n+      pseud_id = @user.default_pseud.id\n+      post \"/api/v1/bookmarks/import\",\n+           { archivist: @user.login,\n+             bookmarks: [ bookmark, bookmark ]\n+           }.to_json,\n+           valid_headers\n+      bookmarks = Bookmark.find_all_by_pseud_id(pseud_id)\n+      assert_equal bookmarks.count, 1\n+    end\n+\n+    it \"should pass back any original references unchanged\" do\n+      post \"/api/v1/bookmarks/import\",\n+           { archivist: @user.login,\n+             bookmarks: [ bookmark ]\n+           }.to_json,\n+           valid_headers\n+      bookmark_response = JSON.parse(response.body, symbolize_names: true)[:bookmarks].first\n+      assert_equal \"123\", bookmark_response[:original_id], \"Original reference should be passed back unchanged\"\n+      assert_equal \"http://foo.com\", bookmark_response[:original_url], \"Original URL should be passed back unchanged\"\n+    end\n+\n+    it \"should respond with the URL of the created bookmark\" do\n+      pseud_id = @user.default_pseud.id\n+      post \"/api/v1/bookmarks/import\",\n+           { archivist: @user.login,\n+             bookmarks: [ bookmark ]\n+           }.to_json,\n+           valid_headers\n+      first_bookmark = Bookmark.find_all_by_pseud_id(pseud_id).first\n+      bookmark_response = JSON.parse(response.body, symbolize_names: true)[:bookmarks].first\n+      assert_equal bookmark_response[:archive_url], bookmark_url(first_bookmark)\n+    end\n+\n+    WebMock.allow_net_connect!\n+  end\n+\n+  describe \"Invalid API bookmark import\" do\n+    before do\n+      mock_external\n+      @user = create(:user)\n+    end\n+\n+    after do\n+      WebMock.reset!\n+    end\n+\n+\n+    it \"should return 400 Bad Request if no bookmarks are specified\" do\n+      post \"/api/v1/bookmarks\",\n+           { archivist: @user.login }.to_json,\n+           valid_headers\n+      assert_equal 400, response.status\n+    end\n+\n+    it \"should return an error mess if no URL is specified\" do\n+      post \"/api/v1/bookmarks\",\n+           { archivist: @user.login,\n+             bookmarks: [ bookmark.except(:url) ]","path":"spec/api/api_bookmarks_spec.rb","position":116,"original_position":116,"commit_id":"d631c110f83a3873d307354badfca999dca54ffd","original_commit_id":"d631c110f83a3873d307354badfca999dca54ffd","user":{"login":"houndci-bot","id":6697940,"avatar_url":"https://avatars.githubusercontent.com/u/6697940?v=3","gravatar_id":"","url":"https://api.github.com/users/houndci-bot","html_url":"https://github.com/houndci-bot","followers_url":"https://api.github.com/users/houndci-bot/followers","following_url":"https://api.github.com/users/houndci-bot/following{/other_user}","gists_url":"https://api.github.com/users/houndci-bot/gists{/gist_id}","starred_url":"https://api.github.com/users/houndci-bot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/houndci-bot/subscriptions","organizations_url":"https://api.github.com/users/houndci-bot/orgs","repos_url":"https://api.github.com/users/houndci-bot/repos","events_url":"https://api.github.com/users/houndci-bot/events{/privacy}","received_events_url":"https://api.github.com/users/houndci-bot/received_events","type":"User","site_admin":false},"body":"Space inside square brackets detected.","created_at":"2016-06-05T21:40:41Z","updated_at":"2016-06-05T21:40:41Z","html_url":"https://github.com/otwcode/otwarchive/pull/2442#discussion_r65825646","pull_request_url":"https://api.github.com/repos/otwcode/otwarchive/pulls/2442","_links":{"self":{"href":"https://api.github.com/repos/otwcode/otwarchive/pulls/comments/65825646"},"html":{"href":"https://github.com/otwcode/otwarchive/pull/2442#discussion_r65825646"},"pull_request":{"href":"https://api.github.com/repos/otwcode/otwarchive/pulls/2442"}}},"pull_request":{"url":"https://api.github.com/repos/otwcode/otwarchive/pulls/2442","id":68237258,"html_url":"https://github.com/otwcode/otwarchive/pull/2442","diff_url":"https://github.com/otwcode/otwarchive/pull/2442.diff","patch_url":"https://github.com/otwcode/otwarchive/pull/2442.patch","issue_url":"https://api.github.com/repos/otwcode/otwarchive/issues/2442","number":2442,"state":"open","locked":false,"title":"AO3-4359 Mass import bookmarks","user":{"login":"ariana-paris","id":1660785,"avatar_url":"https://avatars.githubusercontent.com/u/1660785?v=3","gravatar_id":"","url":"https://api.github.com/users/ariana-paris","html_url":"https://github.com/ariana-paris","followers_url":"https://api.github.com/users/ariana-paris/followers","following_url":"https://api.github.com/users/ariana-paris/following{/other_user}","gists_url":"https://api.github.com/users/ariana-paris/gists{/gist_id}","starred_url":"https://api.github.com/users/ariana-paris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ariana-paris/subscriptions","organizations_url":"https://api.github.com/users/ariana-paris/orgs","repos_url":"https://api.github.com/users/ariana-paris/repos","events_url":"https://api.github.com/users/ariana-paris/events{/privacy}","received_events_url":"https://api.github.com/users/ariana-paris/received_events","type":"User","site_admin":false},"body":"https://otwarchive.atlassian.net/browse/AO3-4359\r\n\r\nAdds bookmark importing capability to the mass import API. Open Doors sometimes import sites which include works that are links to stories on another site which is not being imported (and therefore hasn't granted permission to scrape in the story contents). This will allow those works to be imported as bookmarks on the Archive, referencing the story's currently location rather than importing it.\r\n\r\nIn order to achieve this, I have also:\r\n- refactored the existing code so that the authentication handling and work imports are in separate files\r\n- rationalised the API end points so that instead of `api/v1/import`, we now have `api/v1/works` and `api/v1/bookmarks` (paving the way for, say, `collections`, `users` or, idk, `videos` in future, whatever comes up next)\r\n- added an `original_id` field to all the API requests so the remote site can pass in its own id for the story/bookmark it's importing and use that to match up the Archive's response to the relevant item (currently, the OD temporary sites rely on the responses being in the same order as the requests which is risky)\r\n- converted `routes.rb` to use Ruby 1.9 hashes where possible","created_at":"2016-04-28T15:08:18Z","updated_at":"2016-06-05T21:40:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"370acb46b3b728b7e3a9541abb66db1151b76064","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/otwcode/otwarchive/pulls/2442/commits","review_comments_url":"https://api.github.com/repos/otwcode/otwarchive/pulls/2442/comments","review_comment_url":"https://api.github.com/repos/otwcode/otwarchive/pulls/comments{/number}","comments_url":"https://api.github.com/repos/otwcode/otwarchive/issues/2442/comments","statuses_url":"https://api.github.com/repos/otwcode/otwarchive/statuses/d631c110f83a3873d307354badfca999dca54ffd","head":{"label":"ariana-paris:AO3-4359-Mass-import-bookmarks","ref":"AO3-4359-Mass-import-bookmarks","sha":"d631c110f83a3873d307354badfca999dca54ffd","user":{"login":"ariana-paris","id":1660785,"avatar_url":"https://avatars.githubusercontent.com/u/1660785?v=3","gravatar_id":"","url":"https://api.github.com/users/ariana-paris","html_url":"https://github.com/ariana-paris","followers_url":"https://api.github.com/users/ariana-paris/followers","following_url":"https://api.github.com/users/ariana-paris/following{/other_user}","gists_url":"https://api.github.com/users/ariana-paris/gists{/gist_id}","starred_url":"https://api.github.com/users/ariana-paris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ariana-paris/subscriptions","organizations_url":"https://api.github.com/users/ariana-paris/orgs","repos_url":"https://api.github.com/users/ariana-paris/repos","events_url":"https://api.github.com/users/ariana-paris/events{/privacy}","received_events_url":"https://api.github.com/users/ariana-paris/received_events","type":"User","site_admin":false},"repo":{"id":4213690,"name":"otwarchive","full_name":"ariana-paris/otwarchive","owner":{"login":"ariana-paris","id":1660785,"avatar_url":"https://avatars.githubusercontent.com/u/1660785?v=3","gravatar_id":"","url":"https://api.github.com/users/ariana-paris","html_url":"https://github.com/ariana-paris","followers_url":"https://api.github.com/users/ariana-paris/followers","following_url":"https://api.github.com/users/ariana-paris/following{/other_user}","gists_url":"https://api.github.com/users/ariana-paris/gists{/gist_id}","starred_url":"https://api.github.com/users/ariana-paris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ariana-paris/subscriptions","organizations_url":"https://api.github.com/users/ariana-paris/orgs","repos_url":"https://api.github.com/users/ariana-paris/repos","events_url":"https://api.github.com/users/ariana-paris/events{/privacy}","received_events_url":"https://api.github.com/users/ariana-paris/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ariana-paris/otwarchive","description":"The Organization for Transformative Works (OTW) - Archive Of Our Own (AO3) Project","fork":true,"url":"https://api.github.com/repos/ariana-paris/otwarchive","forks_url":"https://api.github.com/repos/ariana-paris/otwarchive/forks","keys_url":"https://api.github.com/repos/ariana-paris/otwarchive/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ariana-paris/otwarchive/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ariana-paris/otwarchive/teams","hooks_url":"https://api.github.com/repos/ariana-paris/otwarchive/hooks","issue_events_url":"https://api.github.com/repos/ariana-paris/otwarchive/issues/events{/number}","events_url":"https://api.github.com/repos/ariana-paris/otwarchive/events","assignees_url":"https://api.github.com/repos/ariana-paris/otwarchive/assignees{/user}","branches_url":"https://api.github.com/repos/ariana-paris/otwarchive/branches{/branch}","tags_url":"https://api.github.com/repos/ariana-paris/otwarchive/tags","blobs_url":"https://api.github.com/repos/ariana-paris/otwarchive/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ariana-paris/otwarchive/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ariana-paris/otwarchive/git/refs{/sha}","trees_url":"https://api.github.com/repos/ariana-paris/otwarchive/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ariana-paris/otwarchive/statuses/{sha}","languages_url":"https://api.github.com/repos/ariana-paris/otwarchive/languages","stargazers_url":"https://api.github.com/repos/ariana-paris/otwarchive/stargazers","contributors_url":"https://api.github.com/repos/ariana-paris/otwarchive/contributors","subscribers_url":"https://api.github.com/repos/ariana-paris/otwarchive/subscribers","subscription_url":"https://api.github.com/repos/ariana-paris/otwarchive/subscription","commits_url":"https://api.github.com/repos/ariana-paris/otwarchive/commits{/sha}","git_commits_url":"https://api.github.com/repos/ariana-paris/otwarchive/git/commits{/sha}","comments_url":"https://api.github.com/repos/ariana-paris/otwarchive/comments{/number}","issue_comment_url":"https://api.github.com/repos/ariana-paris/otwarchive/issues/comments{/number}","contents_url":"https://api.github.com/repos/ariana-paris/otwarchive/contents/{+path}","compare_url":"https://api.github.com/repos/ariana-paris/otwarchive/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ariana-paris/otwarchive/merges","archive_url":"https://api.github.com/repos/ariana-paris/otwarchive/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ariana-paris/otwarchive/downloads","issues_url":"https://api.github.com/repos/ariana-paris/otwarchive/issues{/number}","pulls_url":"https://api.github.com/repos/ariana-paris/otwarchive/pulls{/number}","milestones_url":"https://api.github.com/repos/ariana-paris/otwarchive/milestones{/number}","notifications_url":"https://api.github.com/repos/ariana-paris/otwarchive/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ariana-paris/otwarchive/labels{/name}","releases_url":"https://api.github.com/repos/ariana-paris/otwarchive/releases{/id}","deployments_url":"https://api.github.com/repos/ariana-paris/otwarchive/deployments","created_at":"2012-05-03T12:27:42Z","updated_at":"2015-03-10T22:17:11Z","pushed_at":"2016-06-05T21:40:35Z","git_url":"git://github.com/ariana-paris/otwarchive.git","ssh_url":"git@github.com:ariana-paris/otwarchive.git","clone_url":"https://github.com/ariana-paris/otwarchive.git","svn_url":"https://github.com/ariana-paris/otwarchive","homepage":"http://transformativeworks.org/projects/archive","size":51608,"stargazers_count":1,"watchers_count":1,"language":"Ruby","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":1,"default_branch":"master"}},"base":{"label":"otwcode:master","ref":"master","sha":"04926aa29d58167aa1c028f79f6273b0db0273ef","user":{"login":"otwcode","id":408606,"avatar_url":"https://avatars.githubusercontent.com/u/408606?v=3","gravatar_id":"","url":"https://api.github.com/users/otwcode","html_url":"https://github.com/otwcode","followers_url":"https://api.github.com/users/otwcode/followers","following_url":"https://api.github.com/users/otwcode/following{/other_user}","gists_url":"https://api.github.com/users/otwcode/gists{/gist_id}","starred_url":"https://api.github.com/users/otwcode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/otwcode/subscriptions","organizations_url":"https://api.github.com/users/otwcode/orgs","repos_url":"https://api.github.com/users/otwcode/repos","events_url":"https://api.github.com/users/otwcode/events{/privacy}","received_events_url":"https://api.github.com/users/otwcode/received_events","type":"Organization","site_admin":false},"repo":{"id":1338040,"name":"otwarchive","full_name":"otwcode/otwarchive","owner":{"login":"otwcode","id":408606,"avatar_url":"https://avatars.githubusercontent.com/u/408606?v=3","gravatar_id":"","url":"https://api.github.com/users/otwcode","html_url":"https://github.com/otwcode","followers_url":"https://api.github.com/users/otwcode/followers","following_url":"https://api.github.com/users/otwcode/following{/other_user}","gists_url":"https://api.github.com/users/otwcode/gists{/gist_id}","starred_url":"https://api.github.com/users/otwcode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/otwcode/subscriptions","organizations_url":"https://api.github.com/users/otwcode/orgs","repos_url":"https://api.github.com/users/otwcode/repos","events_url":"https://api.github.com/users/otwcode/events{/privacy}","received_events_url":"https://api.github.com/users/otwcode/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/otwcode/otwarchive","description":"The Organization for Transformative Works (OTW) - Archive Of Our Own (AO3) Project","fork":false,"url":"https://api.github.com/repos/otwcode/otwarchive","forks_url":"https://api.github.com/repos/otwcode/otwarchive/forks","keys_url":"https://api.github.com/repos/otwcode/otwarchive/keys{/key_id}","collaborators_url":"https://api.github.com/repos/otwcode/otwarchive/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/otwcode/otwarchive/teams","hooks_url":"https://api.github.com/repos/otwcode/otwarchive/hooks","issue_events_url":"https://api.github.com/repos/otwcode/otwarchive/issues/events{/number}","events_url":"https://api.github.com/repos/otwcode/otwarchive/events","assignees_url":"https://api.github.com/repos/otwcode/otwarchive/assignees{/user}","branches_url":"https://api.github.com/repos/otwcode/otwarchive/branches{/branch}","tags_url":"https://api.github.com/repos/otwcode/otwarchive/tags","blobs_url":"https://api.github.com/repos/otwcode/otwarchive/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/otwcode/otwarchive/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/otwcode/otwarchive/git/refs{/sha}","trees_url":"https://api.github.com/repos/otwcode/otwarchive/git/trees{/sha}","statuses_url":"https://api.github.com/repos/otwcode/otwarchive/statuses/{sha}","languages_url":"https://api.github.com/repos/otwcode/otwarchive/languages","stargazers_url":"https://api.github.com/repos/otwcode/otwarchive/stargazers","contributors_url":"https://api.github.com/repos/otwcode/otwarchive/contributors","subscribers_url":"https://api.github.com/repos/otwcode/otwarchive/subscribers","subscription_url":"https://api.github.com/repos/otwcode/otwarchive/subscription","commits_url":"https://api.github.com/repos/otwcode/otwarchive/commits{/sha}","git_commits_url":"https://api.github.com/repos/otwcode/otwarchive/git/commits{/sha}","comments_url":"https://api.github.com/repos/otwcode/otwarchive/comments{/number}","issue_comment_url":"https://api.github.com/repos/otwcode/otwarchive/issues/comments{/number}","contents_url":"https://api.github.com/repos/otwcode/otwarchive/contents/{+path}","compare_url":"https://api.github.com/repos/otwcode/otwarchive/compare/{base}...{head}","merges_url":"https://api.github.com/repos/otwcode/otwarchive/merges","archive_url":"https://api.github.com/repos/otwcode/otwarchive/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/otwcode/otwarchive/downloads","issues_url":"https://api.github.com/repos/otwcode/otwarchive/issues{/number}","pulls_url":"https://api.github.com/repos/otwcode/otwarchive/pulls{/number}","milestones_url":"https://api.github.com/repos/otwcode/otwarchive/milestones{/number}","notifications_url":"https://api.github.com/repos/otwcode/otwarchive/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/otwcode/otwarchive/labels{/name}","releases_url":"https://api.github.com/repos/otwcode/otwarchive/releases{/id}","deployments_url":"https://api.github.com/repos/otwcode/otwarchive/deployments","created_at":"2011-02-07T14:50:58Z","updated_at":"2016-06-05T01:18:11Z","pushed_at":"2016-06-05T21:40:36Z","git_url":"git://github.com/otwcode/otwarchive.git","ssh_url":"git@github.com:otwcode/otwarchive.git","clone_url":"https://github.com/otwcode/otwarchive.git","svn_url":"https://github.com/otwcode/otwarchive","homepage":"http://transformativeworks.org/projects/archive","size":69665,"stargazers_count":200,"watchers_count":200,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":120,"mirror_url":null,"open_issues_count":50,"forks":120,"open_issues":50,"watchers":200,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/otwcode/otwarchive/pulls/2442"},"html":{"href":"https://github.com/otwcode/otwarchive/pull/2442"},"issue":{"href":"https://api.github.com/repos/otwcode/otwarchive/issues/2442"},"comments":{"href":"https://api.github.com/repos/otwcode/otwarchive/issues/2442/comments"},"review_comments":{"href":"https://api.github.com/repos/otwcode/otwarchive/pulls/2442/comments"},"review_comment":{"href":"https://api.github.com/repos/otwcode/otwarchive/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/otwcode/otwarchive/pulls/2442/commits"},"statuses":{"href":"https://api.github.com/repos/otwcode/otwarchive/statuses/d631c110f83a3873d307354badfca999dca54ffd"}}}}
{"actor":{"display_login":"houndci-bot"}}