{"action":"created","comment":{"url":"https://api.github.com/repos/neighborhoods/wasabi/pulls/comments/177801007","pull_request_review_id":107741843,"id":177801007,"diff_hunk":"@@ -0,0 +1,240 @@\n+/**\n+ * Wasabi-LDAP Copyright 2018 Neighborhoods.com\n+ * <p>\n+ * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with\n+ * the License. You may obtain a copy of the License at\n+ * <p>\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ * <p>\n+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on\n+ * an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the\n+ * specific language governing permissions and limitations under the License.\n+ */\n+package com.neighborhoods.wasabi.ldap.impl;\n+\n+import java.util.Date;\n+import java.util.Map;\n+import java.util.Optional;\n+import java.util.concurrent.ConcurrentHashMap;\n+import java.util.stream.Stream;\n+\n+import com.google.inject.Inject;\n+import com.google.inject.Singleton;\n+import com.intuit.wasabi.authenticationobjects.UserInfo;\n+import com.intuit.wasabi.authenticationobjects.UserInfo.Username;\n+import com.intuit.wasabi.exceptions.AuthenticationException;\n+import com.neighborhoods.wasabi.ldap.CachedUserDirectory;\n+import com.neighborhoods.wasabi.ldap.DirectoryDelegate;\n+\n+import static java.text.MessageFormat.format;\n+\n+/**\n+ * The Class LdapUserDirectory.\n+ * \n+ * Concrete implementation of the CachedUserDirectory. This class will store user credentials for a given validity\n+ * period. It will forward all calls to it's delegate for non-cached operations.\n+ * \n+ * Important Note: This class is implemented as a singleton method via Guice's injection model. The below tag is\n+ * critical to it's status as a singleton. Core Wasabi's DefaultUserDirectory class is missing this\n+ */\n+@Singleton\n+public class LdapUserDirectory implements CachedUserDirectory {\n+\n+    /** The Constant ONE_MINUTE_IN_MILLIS. How many milliseconds in one minute. */\n+    protected static final long ONE_MINUTE_IN_MILLIS = 60000;\n+\n+    /** The Constant CACHE_EXPIRY_PERIOD_MINUTES. When the cache should expire. */\n+    protected static final int CACHE_EXPIRY_PERIOD_MINUTES = 90;\n+\n+    /**\n+     * Convenience function to convert a map to stream.\n+     *\n+     * @param <K> the key type\n+     * @param <V> the value type\n+     * @param map the map\n+     * @return the stream\n+     */\n+    private static <K, V> Stream<V> mapToStream(Map<K, V> map) {\n+        return map.values().stream();\n+    }\n+\n+    /** The cached users. */\n+    protected ConcurrentHashMap<String, DirectoryUser> users;\n+\n+    /** The directory delegate. */\n+    protected final DirectoryDelegate delegate;\n+\n+    /**\n+     * Instantiates a new ldap user directory.\n+     *\n+     * @param delegate the delegate\n+     */\n+    @Inject\n+    public LdapUserDirectory(DirectoryDelegate delegate) {\n+        this.delegate = delegate;\n+        refreshCache();\n+    }\n+\n+    /**\n+     * Add user to local cache\n+     * \n+     * @param user The directory user to add to the cache\n+     * @return The result of the cache put operation\n+     */\n+    @Override\n+    public DirectoryUser addUserToCache(DirectoryUser user) {\n+        return this.users.put(user.getUsername().getUsername(), user);\n+    }\n+\n+    /**\n+     * Authenticate (e.g. during initial login) a given user\n+     * \n+     * @param username - The username to authenticate\n+     * @param password - The password to authenticate\n+     * @return The directory user if successful\n+     */\n+    @Override\n+    public DirectoryUser authenticate(String username, String password) {\n+        return delegate.authenticate(this, username, password);\n+    }\n+\n+    /**\n+     * Get a stream of all users\n+     * \n+     * @return All directory users\n+     */\n+    @Override\n+    public Stream<DirectoryUser> getAllUsers() {\n+        Stream<DirectoryUser> result = null;\n+        result = mapToStream(this.users);\n+        return result;\n+    }\n+\n+    /**\n+     * Gets the directory user from the cache.\n+     *\n+     * @param username The target user's username\n+     * @return The directory user object from cache\n+     */\n+    protected DirectoryUser getDirectoryUserFromCache(String username) {\n+        DirectoryUser cachedUser;\n+        cachedUser = this.users.get(username);\n+\n+        if (cachedUser != null) {\n+            return validateCachedUser(cachedUser);\n+        }\n+        return null;\n+    }\n+\n+    /**\n+     * Validate a directory token\n+     * \n+     * @param username The username to validate\n+     * @param encryptedPassword The password to validate\n+     * @return true, if a directory token is valid\n+     */\n+    @Override\n+    public boolean isDirectoryTokenValid(String username, String encryptedPassword) {\n+        return delegate.isDirectoryTokenValid(this, username, encryptedPassword);\n+    }\n+\n+    /**\n+     * Find a user by username\n+     * \n+     * @param username The target user's username\n+     * @return The user object\n+     */\n+    @Override\n+    public DirectoryUser lookupDirectoryUser(String username) {\n+        DirectoryUser result = getDirectoryUserFromCache(username);\n+        if (result != null) {\n+            return result;\n+        }\n+        return delegate.getDirectoryUserByUsername(this, username);\n+    }\n+\n+    /**\n+     * Find a user by username\n+     * \n+     * @param username The username of the target user\n+     * @return The resulting user object\n+     */\n+    @Override\n+    public UserInfo lookupUser(final Username username) {\n+        DirectoryUser result = getDirectoryUserFromCache(username.getUsername());\n+        if (result != null) {\n+            return result.getUserInfo();\n+        }\n+        result = delegate.getDirectoryUserByUsername(this, username.getUsername());\n+        if (result != null) {\n+            return result.getUserInfo();\n+        }\n+        return null;\n+    }\n+\n+    /**\n+     * Lookup a user by email\n+     * \n+     * @param userEmail the target user's email\n+     * @return The resulting user object\n+     */\n+    @Override\n+    public UserInfo lookupUserByEmail(final String userEmail) {\n+        DirectoryUser result = null;\n+        Optional<DirectoryUser> matchingUser = getAllUsers().filter(user -> user.getEmail().equals(userEmail))\n+                .findFirst();\n+        if (matchingUser.isPresent()) {\n+            result = validateCachedUser(matchingUser.get());\n+        }\n+        if (result == null) {\n+            result = delegate.getDirectoryUserByEmail(this, userEmail);\n+        }\n+        if (result != null) {\n+            return result.getUserInfo();\n+        }\n+        throw new AuthenticationException(format(\"Email address does not exist: {0}\", userEmail));\n+    }\n+\n+    /**\n+     * Refresh the cache\n+     */\n+    @Override\n+    public void refreshCache() {\n+        if (this.users == null) {\n+            this.users = new ConcurrentHashMap<String, DirectoryUser>();\n+        } else {\n+            this.users.clear();\n+        }\n+        delegate.populateUserCache(this);\n+    }\n+\n+    /**\n+     * Remove a user from the cache\n+     * \n+     * @param The username of the user to remove\n+     * @return The result of the remove action\n+     */\n+    @Override\n+    public DirectoryUser removeUserFromCache(String username) {\n+        return this.users.remove(username);\n+    }\n+\n+    /**\n+     * Validate a cached user (check expiration)\n+     *\n+     * @param cachedUser the cached user\n+     * @return the directory user\n+     */\n+    protected DirectoryUser validateCachedUser(DirectoryUser cachedUser) {","path":"modules/ldap/src/main/java/com/neighborhoods/wasabi/ldap/impl/LdapUserDirectory.java","position":242,"original_position":228,"commit_id":"e4ffbf40a4d40490a894f04d893901a2e4b1d428","original_commit_id":"f4e2a070768da684a71aeb74ebfe167942654a83","user":{"login":"arielallon","id":1356396,"avatar_url":"https://avatars1.githubusercontent.com/u/1356396?v=4","gravatar_id":"","url":"https://api.github.com/users/arielallon","html_url":"https://github.com/arielallon","followers_url":"https://api.github.com/users/arielallon/followers","following_url":"https://api.github.com/users/arielallon/following{/other_user}","gists_url":"https://api.github.com/users/arielallon/gists{/gist_id}","starred_url":"https://api.github.com/users/arielallon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arielallon/subscriptions","organizations_url":"https://api.github.com/users/arielallon/orgs","repos_url":"https://api.github.com/users/arielallon/repos","events_url":"https://api.github.com/users/arielallon/events{/privacy}","received_events_url":"https://api.github.com/users/arielallon/received_events","type":"User","site_admin":false},"body":"yea i'm on board with retaining the signature/contract and relying on naming for clarity.\r\n@nafarius333's option is pretty good.\r\nthe current name actually works ok if you understand \"validation\" as a verb acting on and potentially mutating the `cachedUser`, so i'm ok with it too.","created_at":"2018-03-28T15:58:52Z","updated_at":"2018-03-28T15:58:52Z","html_url":"https://github.com/neighborhoods/wasabi/pull/1#discussion_r177801007","pull_request_url":"https://api.github.com/repos/neighborhoods/wasabi/pulls/1","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/neighborhoods/wasabi/pulls/comments/177801007"},"html":{"href":"https://github.com/neighborhoods/wasabi/pull/1#discussion_r177801007"},"pull_request":{"href":"https://api.github.com/repos/neighborhoods/wasabi/pulls/1"}},"in_reply_to_id":174684766},"pull_request":{"url":"https://api.github.com/repos/neighborhoods/wasabi/pulls/1","id":162646719,"html_url":"https://github.com/neighborhoods/wasabi/pull/1","diff_url":"https://github.com/neighborhoods/wasabi/pull/1.diff","patch_url":"https://github.com/neighborhoods/wasabi/pull/1.patch","issue_url":"https://api.github.com/repos/neighborhoods/wasabi/issues/1","number":1,"state":"open","locked":false,"title":"DATA-202 | Wasabi LDAP support","user":{"login":"jlab55","id":32461539,"avatar_url":"https://avatars3.githubusercontent.com/u/32461539?v=4","gravatar_id":"","url":"https://api.github.com/users/jlab55","html_url":"https://github.com/jlab55","followers_url":"https://api.github.com/users/jlab55/followers","following_url":"https://api.github.com/users/jlab55/following{/other_user}","gists_url":"https://api.github.com/users/jlab55/gists{/gist_id}","starred_url":"https://api.github.com/users/jlab55/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jlab55/subscriptions","organizations_url":"https://api.github.com/users/jlab55/orgs","repos_url":"https://api.github.com/users/jlab55/repos","events_url":"https://api.github.com/users/jlab55/events{/privacy}","received_events_url":"https://api.github.com/users/jlab55/received_events","type":"User","site_admin":false},"body":"Just getting an initial PR out there so you can see the related code (if you want) prior to our overview meeting on Monday. No need to review or comment yet. \r\n\r\nAlso subject to me being a perfectionist and tweaking things before Monday. I haven't actually closed out my whole to-do list on this.\r\n\r\nAll of this will not actually be merged to this branch but instead to the public repo.\r\n\r\n","created_at":"2018-01-12T14:45:09Z","updated_at":"2018-03-28T15:58:52Z","closed_at":null,"merged_at":null,"merge_commit_sha":"294207aa309fe913d8e65e1743ab9812a742863c","assignee":{"login":"jlab55","id":32461539,"avatar_url":"https://avatars3.githubusercontent.com/u/32461539?v=4","gravatar_id":"","url":"https://api.github.com/users/jlab55","html_url":"https://github.com/jlab55","followers_url":"https://api.github.com/users/jlab55/followers","following_url":"https://api.github.com/users/jlab55/following{/other_user}","gists_url":"https://api.github.com/users/jlab55/gists{/gist_id}","starred_url":"https://api.github.com/users/jlab55/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jlab55/subscriptions","organizations_url":"https://api.github.com/users/jlab55/orgs","repos_url":"https://api.github.com/users/jlab55/repos","events_url":"https://api.github.com/users/jlab55/events{/privacy}","received_events_url":"https://api.github.com/users/jlab55/received_events","type":"User","site_admin":false},"assignees":[{"login":"jlab55","id":32461539,"avatar_url":"https://avatars3.githubusercontent.com/u/32461539?v=4","gravatar_id":"","url":"https://api.github.com/users/jlab55","html_url":"https://github.com/jlab55","followers_url":"https://api.github.com/users/jlab55/followers","following_url":"https://api.github.com/users/jlab55/following{/other_user}","gists_url":"https://api.github.com/users/jlab55/gists{/gist_id}","starred_url":"https://api.github.com/users/jlab55/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jlab55/subscriptions","organizations_url":"https://api.github.com/users/jlab55/orgs","repos_url":"https://api.github.com/users/jlab55/repos","events_url":"https://api.github.com/users/jlab55/events{/privacy}","received_events_url":"https://api.github.com/users/jlab55/received_events","type":"User","site_admin":false}],"requested_reviewers":[{"login":"arielallon","id":1356396,"avatar_url":"https://avatars1.githubusercontent.com/u/1356396?v=4","gravatar_id":"","url":"https://api.github.com/users/arielallon","html_url":"https://github.com/arielallon","followers_url":"https://api.github.com/users/arielallon/followers","following_url":"https://api.github.com/users/arielallon/following{/other_user}","gists_url":"https://api.github.com/users/arielallon/gists{/gist_id}","starred_url":"https://api.github.com/users/arielallon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arielallon/subscriptions","organizations_url":"https://api.github.com/users/arielallon/orgs","repos_url":"https://api.github.com/users/arielallon/repos","events_url":"https://api.github.com/users/arielallon/events{/privacy}","received_events_url":"https://api.github.com/users/arielallon/received_events","type":"User","site_admin":false},{"login":"patrickf55places","id":29067405,"avatar_url":"https://avatars2.githubusercontent.com/u/29067405?v=4","gravatar_id":"","url":"https://api.github.com/users/patrickf55places","html_url":"https://github.com/patrickf55places","followers_url":"https://api.github.com/users/patrickf55places/followers","following_url":"https://api.github.com/users/patrickf55places/following{/other_user}","gists_url":"https://api.github.com/users/patrickf55places/gists{/gist_id}","starred_url":"https://api.github.com/users/patrickf55places/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patrickf55places/subscriptions","organizations_url":"https://api.github.com/users/patrickf55places/orgs","repos_url":"https://api.github.com/users/patrickf55places/repos","events_url":"https://api.github.com/users/patrickf55places/events{/privacy}","received_events_url":"https://api.github.com/users/patrickf55places/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[{"id":802902904,"url":"https://api.github.com/repos/neighborhoods/wasabi/labels/Do%20not%20merge","name":"Do not merge","color":"b7101b","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/neighborhoods/wasabi/pulls/1/commits","review_comments_url":"https://api.github.com/repos/neighborhoods/wasabi/pulls/1/comments","review_comment_url":"https://api.github.com/repos/neighborhoods/wasabi/pulls/comments{/number}","comments_url":"https://api.github.com/repos/neighborhoods/wasabi/issues/1/comments","statuses_url":"https://api.github.com/repos/neighborhoods/wasabi/statuses/e4ffbf40a4d40490a894f04d893901a2e4b1d428","head":{"label":"neighborhoods:feature/DATA-202","ref":"feature/DATA-202","sha":"e4ffbf40a4d40490a894f04d893901a2e4b1d428","user":{"login":"neighborhoods","id":8584765,"avatar_url":"https://avatars3.githubusercontent.com/u/8584765?v=4","gravatar_id":"","url":"https://api.github.com/users/neighborhoods","html_url":"https://github.com/neighborhoods","followers_url":"https://api.github.com/users/neighborhoods/followers","following_url":"https://api.github.com/users/neighborhoods/following{/other_user}","gists_url":"https://api.github.com/users/neighborhoods/gists{/gist_id}","starred_url":"https://api.github.com/users/neighborhoods/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/neighborhoods/subscriptions","organizations_url":"https://api.github.com/users/neighborhoods/orgs","repos_url":"https://api.github.com/users/neighborhoods/repos","events_url":"https://api.github.com/users/neighborhoods/events{/privacy}","received_events_url":"https://api.github.com/users/neighborhoods/received_events","type":"Organization","site_admin":false},"repo":{"id":94116056,"name":"wasabi","full_name":"neighborhoods/wasabi","owner":{"login":"neighborhoods","id":8584765,"avatar_url":"https://avatars3.githubusercontent.com/u/8584765?v=4","gravatar_id":"","url":"https://api.github.com/users/neighborhoods","html_url":"https://github.com/neighborhoods","followers_url":"https://api.github.com/users/neighborhoods/followers","following_url":"https://api.github.com/users/neighborhoods/following{/other_user}","gists_url":"https://api.github.com/users/neighborhoods/gists{/gist_id}","starred_url":"https://api.github.com/users/neighborhoods/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/neighborhoods/subscriptions","organizations_url":"https://api.github.com/users/neighborhoods/orgs","repos_url":"https://api.github.com/users/neighborhoods/repos","events_url":"https://api.github.com/users/neighborhoods/events{/privacy}","received_events_url":"https://api.github.com/users/neighborhoods/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/neighborhoods/wasabi","description":"Wasabi A/B Testing Service is a real-time, enterprise-grade, 100% API driven project.","fork":true,"url":"https://api.github.com/repos/neighborhoods/wasabi","forks_url":"https://api.github.com/repos/neighborhoods/wasabi/forks","keys_url":"https://api.github.com/repos/neighborhoods/wasabi/keys{/key_id}","collaborators_url":"https://api.github.com/repos/neighborhoods/wasabi/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/neighborhoods/wasabi/teams","hooks_url":"https://api.github.com/repos/neighborhoods/wasabi/hooks","issue_events_url":"https://api.github.com/repos/neighborhoods/wasabi/issues/events{/number}","events_url":"https://api.github.com/repos/neighborhoods/wasabi/events","assignees_url":"https://api.github.com/repos/neighborhoods/wasabi/assignees{/user}","branches_url":"https://api.github.com/repos/neighborhoods/wasabi/branches{/branch}","tags_url":"https://api.github.com/repos/neighborhoods/wasabi/tags","blobs_url":"https://api.github.com/repos/neighborhoods/wasabi/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/neighborhoods/wasabi/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/neighborhoods/wasabi/git/refs{/sha}","trees_url":"https://api.github.com/repos/neighborhoods/wasabi/git/trees{/sha}","statuses_url":"https://api.github.com/repos/neighborhoods/wasabi/statuses/{sha}","languages_url":"https://api.github.com/repos/neighborhoods/wasabi/languages","stargazers_url":"https://api.github.com/repos/neighborhoods/wasabi/stargazers","contributors_url":"https://api.github.com/repos/neighborhoods/wasabi/contributors","subscribers_url":"https://api.github.com/repos/neighborhoods/wasabi/subscribers","subscription_url":"https://api.github.com/repos/neighborhoods/wasabi/subscription","commits_url":"https://api.github.com/repos/neighborhoods/wasabi/commits{/sha}","git_commits_url":"https://api.github.com/repos/neighborhoods/wasabi/git/commits{/sha}","comments_url":"https://api.github.com/repos/neighborhoods/wasabi/comments{/number}","issue_comment_url":"https://api.github.com/repos/neighborhoods/wasabi/issues/comments{/number}","contents_url":"https://api.github.com/repos/neighborhoods/wasabi/contents/{+path}","compare_url":"https://api.github.com/repos/neighborhoods/wasabi/compare/{base}...{head}","merges_url":"https://api.github.com/repos/neighborhoods/wasabi/merges","archive_url":"https://api.github.com/repos/neighborhoods/wasabi/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/neighborhoods/wasabi/downloads","issues_url":"https://api.github.com/repos/neighborhoods/wasabi/issues{/number}","pulls_url":"https://api.github.com/repos/neighborhoods/wasabi/pulls{/number}","milestones_url":"https://api.github.com/repos/neighborhoods/wasabi/milestones{/number}","notifications_url":"https://api.github.com/repos/neighborhoods/wasabi/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/neighborhoods/wasabi/labels{/name}","releases_url":"https://api.github.com/repos/neighborhoods/wasabi/releases{/id}","deployments_url":"https://api.github.com/repos/neighborhoods/wasabi/deployments","created_at":"2017-06-12T16:12:30Z","updated_at":"2017-06-12T16:12:35Z","pushed_at":"2018-03-23T16:24:21Z","git_url":"git://github.com/neighborhoods/wasabi.git","ssh_url":"git@github.com:neighborhoods/wasabi.git","clone_url":"https://github.com/neighborhoods/wasabi.git","svn_url":"https://github.com/neighborhoods/wasabi","homepage":"","size":18993,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":null,"forks":0,"open_issues":1,"watchers":0,"default_branch":"develop"}},"base":{"label":"neighborhoods:develop","ref":"develop","sha":"21962872c36b79222307efab817ccb3455b7259c","user":{"login":"neighborhoods","id":8584765,"avatar_url":"https://avatars3.githubusercontent.com/u/8584765?v=4","gravatar_id":"","url":"https://api.github.com/users/neighborhoods","html_url":"https://github.com/neighborhoods","followers_url":"https://api.github.com/users/neighborhoods/followers","following_url":"https://api.github.com/users/neighborhoods/following{/other_user}","gists_url":"https://api.github.com/users/neighborhoods/gists{/gist_id}","starred_url":"https://api.github.com/users/neighborhoods/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/neighborhoods/subscriptions","organizations_url":"https://api.github.com/users/neighborhoods/orgs","repos_url":"https://api.github.com/users/neighborhoods/repos","events_url":"https://api.github.com/users/neighborhoods/events{/privacy}","received_events_url":"https://api.github.com/users/neighborhoods/received_events","type":"Organization","site_admin":false},"repo":{"id":94116056,"name":"wasabi","full_name":"neighborhoods/wasabi","owner":{"login":"neighborhoods","id":8584765,"avatar_url":"https://avatars3.githubusercontent.com/u/8584765?v=4","gravatar_id":"","url":"https://api.github.com/users/neighborhoods","html_url":"https://github.com/neighborhoods","followers_url":"https://api.github.com/users/neighborhoods/followers","following_url":"https://api.github.com/users/neighborhoods/following{/other_user}","gists_url":"https://api.github.com/users/neighborhoods/gists{/gist_id}","starred_url":"https://api.github.com/users/neighborhoods/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/neighborhoods/subscriptions","organizations_url":"https://api.github.com/users/neighborhoods/orgs","repos_url":"https://api.github.com/users/neighborhoods/repos","events_url":"https://api.github.com/users/neighborhoods/events{/privacy}","received_events_url":"https://api.github.com/users/neighborhoods/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/neighborhoods/wasabi","description":"Wasabi A/B Testing Service is a real-time, enterprise-grade, 100% API driven project.","fork":true,"url":"https://api.github.com/repos/neighborhoods/wasabi","forks_url":"https://api.github.com/repos/neighborhoods/wasabi/forks","keys_url":"https://api.github.com/repos/neighborhoods/wasabi/keys{/key_id}","collaborators_url":"https://api.github.com/repos/neighborhoods/wasabi/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/neighborhoods/wasabi/teams","hooks_url":"https://api.github.com/repos/neighborhoods/wasabi/hooks","issue_events_url":"https://api.github.com/repos/neighborhoods/wasabi/issues/events{/number}","events_url":"https://api.github.com/repos/neighborhoods/wasabi/events","assignees_url":"https://api.github.com/repos/neighborhoods/wasabi/assignees{/user}","branches_url":"https://api.github.com/repos/neighborhoods/wasabi/branches{/branch}","tags_url":"https://api.github.com/repos/neighborhoods/wasabi/tags","blobs_url":"https://api.github.com/repos/neighborhoods/wasabi/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/neighborhoods/wasabi/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/neighborhoods/wasabi/git/refs{/sha}","trees_url":"https://api.github.com/repos/neighborhoods/wasabi/git/trees{/sha}","statuses_url":"https://api.github.com/repos/neighborhoods/wasabi/statuses/{sha}","languages_url":"https://api.github.com/repos/neighborhoods/wasabi/languages","stargazers_url":"https://api.github.com/repos/neighborhoods/wasabi/stargazers","contributors_url":"https://api.github.com/repos/neighborhoods/wasabi/contributors","subscribers_url":"https://api.github.com/repos/neighborhoods/wasabi/subscribers","subscription_url":"https://api.github.com/repos/neighborhoods/wasabi/subscription","commits_url":"https://api.github.com/repos/neighborhoods/wasabi/commits{/sha}","git_commits_url":"https://api.github.com/repos/neighborhoods/wasabi/git/commits{/sha}","comments_url":"https://api.github.com/repos/neighborhoods/wasabi/comments{/number}","issue_comment_url":"https://api.github.com/repos/neighborhoods/wasabi/issues/comments{/number}","contents_url":"https://api.github.com/repos/neighborhoods/wasabi/contents/{+path}","compare_url":"https://api.github.com/repos/neighborhoods/wasabi/compare/{base}...{head}","merges_url":"https://api.github.com/repos/neighborhoods/wasabi/merges","archive_url":"https://api.github.com/repos/neighborhoods/wasabi/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/neighborhoods/wasabi/downloads","issues_url":"https://api.github.com/repos/neighborhoods/wasabi/issues{/number}","pulls_url":"https://api.github.com/repos/neighborhoods/wasabi/pulls{/number}","milestones_url":"https://api.github.com/repos/neighborhoods/wasabi/milestones{/number}","notifications_url":"https://api.github.com/repos/neighborhoods/wasabi/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/neighborhoods/wasabi/labels{/name}","releases_url":"https://api.github.com/repos/neighborhoods/wasabi/releases{/id}","deployments_url":"https://api.github.com/repos/neighborhoods/wasabi/deployments","created_at":"2017-06-12T16:12:30Z","updated_at":"2017-06-12T16:12:35Z","pushed_at":"2018-03-23T16:24:21Z","git_url":"git://github.com/neighborhoods/wasabi.git","ssh_url":"git@github.com:neighborhoods/wasabi.git","clone_url":"https://github.com/neighborhoods/wasabi.git","svn_url":"https://github.com/neighborhoods/wasabi","homepage":"","size":18993,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":null,"forks":0,"open_issues":1,"watchers":0,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/neighborhoods/wasabi/pulls/1"},"html":{"href":"https://github.com/neighborhoods/wasabi/pull/1"},"issue":{"href":"https://api.github.com/repos/neighborhoods/wasabi/issues/1"},"comments":{"href":"https://api.github.com/repos/neighborhoods/wasabi/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/neighborhoods/wasabi/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/neighborhoods/wasabi/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/neighborhoods/wasabi/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/neighborhoods/wasabi/statuses/e4ffbf40a4d40490a894f04d893901a2e4b1d428"}},"author_association":"NONE"}}
{"actor":{"display_login":"arielallon"}}